@burdenoff/microfe-organization 2026.1026.1 → 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.
- package/dist/generated/global-operations.js +57 -58
- package/dist/generated/global-operations.js.map +1 -1
- package/dist/generated/global-types.js +1 -2
- package/dist/generated/global-types.js.map +1 -1
- package/dist/node_modules/sonner/dist/index.js +2 -3
- package/dist/node_modules/sonner/dist/index.js.map +1 -1
- package/dist/node_modules/zod/v4/classic/errors.js +44 -0
- package/dist/node_modules/zod/v4/classic/errors.js.map +1 -0
- package/dist/node_modules/zod/v4/classic/parse.js +7 -0
- package/dist/node_modules/zod/v4/classic/parse.js.map +1 -0
- package/dist/node_modules/zod/v4/classic/schemas.js +652 -0
- package/dist/node_modules/zod/v4/classic/schemas.js.map +1 -0
- package/dist/node_modules/zod/v4/core/api.js +429 -0
- package/dist/node_modules/zod/v4/core/api.js.map +1 -0
- package/dist/node_modules/zod/v4/core/checks.js +143 -0
- package/dist/node_modules/zod/v4/core/checks.js.map +1 -0
- package/dist/node_modules/zod/v4/core/core.js +84 -0
- package/dist/node_modules/zod/v4/core/core.js.map +1 -0
- package/dist/node_modules/zod/v4/core/doc.js +29 -0
- package/dist/node_modules/zod/v4/core/doc.js.map +1 -0
- package/dist/node_modules/zod/v4/core/errors.js +90 -0
- package/dist/node_modules/zod/v4/core/errors.js.map +1 -0
- package/dist/node_modules/zod/v4/core/json-schema-processors.js +285 -0
- package/dist/node_modules/zod/v4/core/json-schema-processors.js.map +1 -0
- package/dist/node_modules/zod/v4/core/memoizer.js +188 -0
- package/dist/node_modules/zod/v4/core/memoizer.js.map +1 -0
- package/dist/node_modules/zod/v4/core/parse.js +159 -0
- package/dist/node_modules/zod/v4/core/parse.js.map +1 -0
- package/dist/node_modules/zod/v4/core/regexes.js +34 -0
- package/dist/node_modules/zod/v4/core/regexes.js.map +1 -0
- package/dist/node_modules/zod/v4/core/registries.js +41 -0
- package/dist/node_modules/zod/v4/core/registries.js.map +1 -0
- package/dist/node_modules/zod/v4/core/schemas.js +979 -0
- package/dist/node_modules/zod/v4/core/schemas.js.map +1 -0
- package/dist/node_modules/zod/v4/core/to-json-schema.js +330 -0
- package/dist/node_modules/zod/v4/core/to-json-schema.js.map +1 -0
- package/dist/node_modules/zod/v4/core/util.js +417 -0
- package/dist/node_modules/zod/v4/core/util.js.map +1 -0
- package/dist/node_modules/zod/v4/core/versions.js +9 -0
- package/dist/node_modules/zod/v4/core/versions.js.map +1 -0
- package/dist/node_modules/zod/v4/locales/en.js +96 -0
- package/dist/node_modules/zod/v4/locales/en.js.map +1 -0
- package/dist/organization-management/AdminOrganizationRoot.js +13 -10
- package/dist/organization-management/AdminOrganizationRoot.js.map +1 -1
- package/dist/organization-management/AdminOrganizationRoutes.js +8 -9
- package/dist/organization-management/AdminOrganizationRoutes.js.map +1 -1
- package/dist/organization-management/UserOrganizationRoot.js +13 -10
- package/dist/organization-management/UserOrganizationRoot.js.map +1 -1
- package/dist/organization-management/UserOrganizationRoutes.js +1 -2
- package/dist/organization-management/UserOrganizationRoutes.js.map +1 -1
- package/dist/organization-management/constants/permissions.js +1 -2
- package/dist/organization-management/constants/permissions.js.map +1 -1
- package/dist/organization-management/hooks/useOrganizationEventEmitter.js +1 -2
- package/dist/organization-management/hooks/useOrganizationEventEmitter.js.map +1 -1
- package/dist/organization-management/hooks/useOrganizationPageView.js +1 -2
- package/dist/organization-management/hooks/useOrganizationPageView.js.map +1 -1
- package/dist/organization-management/modules/members/components/BulkInviteForm.js +3 -3
- package/dist/organization-management/modules/members/components/BulkInviteForm.js.map +1 -1
- package/dist/organization-management/modules/members/components/InviteMemberForm.js +1 -2
- package/dist/organization-management/modules/members/components/InviteMemberForm.js.map +1 -1
- package/dist/organization-management/modules/members/components/InviteStatusBadge.js +1 -2
- package/dist/organization-management/modules/members/components/InviteStatusBadge.js.map +1 -1
- package/dist/organization-management/modules/members/components/MemberRoleBadge.js +1 -2
- package/dist/organization-management/modules/members/components/MemberRoleBadge.js.map +1 -1
- package/dist/organization-management/modules/members/components/MyInviteCard.js +5 -6
- package/dist/organization-management/modules/members/components/MyInviteCard.js.map +1 -1
- package/dist/organization-management/modules/members/components/PendingInvitesList.js +8 -9
- package/dist/organization-management/modules/members/components/PendingInvitesList.js.map +1 -1
- package/dist/organization-management/modules/members/hooks/useInvites.js +2 -3
- package/dist/organization-management/modules/members/hooks/useInvites.js.map +1 -1
- package/dist/organization-management/modules/members/hooks/useMembers.js +1 -2
- package/dist/organization-management/modules/members/hooks/useMembers.js.map +1 -1
- package/dist/organization-management/modules/members/hooks/useWorkspaceInvites.js +1 -2
- package/dist/organization-management/modules/members/hooks/useWorkspaceInvites.js.map +1 -1
- package/dist/organization-management/modules/members/pages/InviteMemberPage.js +16 -17
- package/dist/organization-management/modules/members/pages/InviteMemberPage.js.map +1 -1
- package/dist/organization-management/modules/members/pages/InvitePeoplePage.js +14 -15
- package/dist/organization-management/modules/members/pages/InvitePeoplePage.js.map +1 -1
- package/dist/organization-management/modules/members/pages/MemberDetailPage.js +18 -19
- package/dist/organization-management/modules/members/pages/MemberDetailPage.js.map +1 -1
- package/dist/organization-management/modules/members/pages/MembersListPage.js +142 -143
- package/dist/organization-management/modules/members/pages/MembersListPage.js.map +1 -1
- package/dist/organization-management/modules/members/pages/MyInvitesPage.js +168 -169
- package/dist/organization-management/modules/members/pages/MyInvitesPage.js.map +1 -1
- package/dist/organization-management/modules/members/pages/OrganizationInviteAcceptancePage.js +13 -14
- package/dist/organization-management/modules/members/pages/OrganizationInviteAcceptancePage.js.map +1 -1
- package/dist/organization-management/modules/members/utils/bulkInviteCsv.js +4 -10
- package/dist/organization-management/modules/members/utils/bulkInviteCsv.js.map +1 -1
- package/dist/organization-management/modules/members/utils/workspaceInvitationDisplay.js +1 -2
- package/dist/organization-management/modules/members/utils/workspaceInvitationDisplay.js.map +1 -1
- package/dist/organization-management/modules/organizations/components/OrganizationForm.js +14 -15
- package/dist/organization-management/modules/organizations/components/OrganizationForm.js.map +1 -1
- package/dist/organization-management/modules/organizations/components/OrganizationStatusBadge.js +1 -2
- package/dist/organization-management/modules/organizations/components/OrganizationStatusBadge.js.map +1 -1
- package/dist/organization-management/modules/organizations/hooks/useOrganizations.js +2 -3
- package/dist/organization-management/modules/organizations/hooks/useOrganizations.js.map +1 -1
- package/dist/organization-management/modules/organizations/hooks/useOrgsWithPermission.js +22 -21
- package/dist/organization-management/modules/organizations/hooks/useOrgsWithPermission.js.map +1 -1
- package/dist/organization-management/modules/organizations/pages/OrganizationCreatePage.js +18 -19
- package/dist/organization-management/modules/organizations/pages/OrganizationCreatePage.js.map +1 -1
- package/dist/organization-management/modules/organizations/pages/OrganizationDetailPage.js +29 -30
- package/dist/organization-management/modules/organizations/pages/OrganizationDetailPage.js.map +1 -1
- package/dist/organization-management/modules/organizations/pages/OrganizationEditPage.js +14 -15
- package/dist/organization-management/modules/organizations/pages/OrganizationEditPage.js.map +1 -1
- package/dist/organization-management/modules/organizations/pages/OrganizationsListPage.js +33 -34
- package/dist/organization-management/modules/organizations/pages/OrganizationsListPage.js.map +1 -1
- package/dist/organization-management/modules/organizations/schemas/index.js +11 -12
- package/dist/organization-management/modules/organizations/schemas/index.js.map +1 -1
- package/dist/organization-management/modules/workspaces/components/AddWorkspaceMemberDialog.js +16 -24
- package/dist/organization-management/modules/workspaces/components/AddWorkspaceMemberDialog.js.map +1 -1
- package/dist/organization-management/modules/workspaces/components/WorkspaceCard.js +5 -6
- package/dist/organization-management/modules/workspaces/components/WorkspaceCard.js.map +1 -1
- package/dist/organization-management/modules/workspaces/components/WorkspaceForm.js +31 -32
- package/dist/organization-management/modules/workspaces/components/WorkspaceForm.js.map +1 -1
- package/dist/organization-management/modules/workspaces/components/WorkspaceStatusBadge.js +1 -2
- package/dist/organization-management/modules/workspaces/components/WorkspaceStatusBadge.js.map +1 -1
- package/dist/organization-management/modules/workspaces/hooks/useWorkspaceMembers.js +1 -2
- package/dist/organization-management/modules/workspaces/hooks/useWorkspaceMembers.js.map +1 -1
- package/dist/organization-management/modules/workspaces/hooks/useWorkspaces.js +11 -12
- package/dist/organization-management/modules/workspaces/hooks/useWorkspaces.js.map +1 -1
- package/dist/organization-management/modules/workspaces/pages/WorkspaceCreatePage.js +18 -19
- package/dist/organization-management/modules/workspaces/pages/WorkspaceCreatePage.js.map +1 -1
- package/dist/organization-management/modules/workspaces/pages/WorkspaceDetailPage.js +43 -44
- package/dist/organization-management/modules/workspaces/pages/WorkspaceDetailPage.js.map +1 -1
- package/dist/organization-management/modules/workspaces/pages/WorkspaceEditPage.js +19 -20
- package/dist/organization-management/modules/workspaces/pages/WorkspaceEditPage.js.map +1 -1
- package/dist/organization-management/modules/workspaces/pages/WorkspacesListPage.js +240 -241
- package/dist/organization-management/modules/workspaces/pages/WorkspacesListPage.js.map +1 -1
- package/dist/organization-management/modules/workspaces/schemas/index.js +6 -7
- package/dist/organization-management/modules/workspaces/schemas/index.js.map +1 -1
- package/dist/organization-management/providers/OrgConsoleProvider.js +1 -2
- package/dist/organization-management/providers/OrgConsoleProvider.js.map +1 -1
- package/dist/organization-management/providers/OrgScopedPermissions.js +1 -2
- package/dist/organization-management/providers/OrgScopedPermissions.js.map +1 -1
- package/dist/organization-management/shared/components/dialog/ConfirmDialog.js +1 -2
- package/dist/organization-management/shared/components/dialog/ConfirmDialog.js.map +1 -1
- package/dist/organization-management/shared/components/dialog/ConfirmDialogView.js +1 -2
- package/dist/organization-management/shared/components/dialog/ConfirmDialogView.js.map +1 -1
- package/dist/organization-management/shared/components/feedback/ErrorState.js +1 -2
- package/dist/organization-management/shared/components/feedback/ErrorState.js.map +1 -1
- package/dist/organization-management/shared/components/feedback/LoadingState.js +1 -2
- package/dist/organization-management/shared/components/feedback/LoadingState.js.map +1 -1
- package/dist/organization-management/shared/components/input/SearchInput.js +1 -2
- package/dist/organization-management/shared/components/input/SearchInput.js.map +1 -1
- package/dist/organization-management/shared/components/input/SelectionCheckbox.js +1 -2
- package/dist/organization-management/shared/components/input/SelectionCheckbox.js.map +1 -1
- package/dist/organization-management/shared/hooks/useDebouncedValue.js +1 -2
- package/dist/organization-management/shared/hooks/useDebouncedValue.js.map +1 -1
- package/dist/organization-management/shared/hooks/useTr.js +1 -2
- package/dist/organization-management/shared/hooks/useTr.js.map +1 -1
- package/dist/organization-management/shared/logger.js +1 -2
- package/dist/organization-management/shared/logger.js.map +1 -1
- package/dist/organization-management/shared/recaptcha.js +1 -2
- package/dist/organization-management/shared/recaptcha.js.map +1 -1
- package/dist/organization-management/shared/types/graphql.js +1 -2
- package/dist/organization-management/shared/types/graphql.js.map +1 -1
- package/dist/utils/nativeBridge.js +1 -2
- package/package.json +7 -6
- package/dist/node_modules/zod/v3/ZodError.js +0 -82
- package/dist/node_modules/zod/v3/ZodError.js.map +0 -1
- package/dist/node_modules/zod/v3/errors.js +0 -10
- package/dist/node_modules/zod/v3/errors.js.map +0 -1
- package/dist/node_modules/zod/v3/helpers/errorUtil.js +0 -9
- package/dist/node_modules/zod/v3/helpers/errorUtil.js.map +0 -1
- package/dist/node_modules/zod/v3/helpers/parseUtil.js +0 -93
- package/dist/node_modules/zod/v3/helpers/parseUtil.js.map +0 -1
- package/dist/node_modules/zod/v3/helpers/util.js +0 -76
- package/dist/node_modules/zod/v3/helpers/util.js.map +0 -1
- package/dist/node_modules/zod/v3/locales/en.js +0 -62
- package/dist/node_modules/zod/v3/locales/en.js.map +0 -1
- package/dist/node_modules/zod/v3/types.js +0 -2441
- package/dist/node_modules/zod/v3/types.js.map +0 -1
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { OrgConsoleProvider as e } from "./providers/OrgConsoleProvider.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { AdminOrganizationRoutes as t } from "./AdminOrganizationRoutes.js";
|
|
3
|
+
import { useTr as n } from "./shared/hooks/useTr.js";
|
|
4
4
|
import { useEffect as r } from "react";
|
|
5
5
|
import { Briefcase as i, Building as a, Mail as o, Users as s } from "lucide-react";
|
|
6
6
|
import { Toaster as c } from "@burdenoff/fe-libs/ui";
|
|
7
7
|
import { FeatureFlagProvider as l } from "@burdenoff/fe-libs/shared/feature-flags";
|
|
8
8
|
import { jsx as u, jsxs as d } from "react/jsx-runtime";
|
|
9
|
-
|
|
9
|
+
// src/organization-management/AdminOrganizationRoot.tsx
|
|
10
10
|
var f = (f) => {
|
|
11
|
-
let { registerNavItems: p, registerFooterItems: m, basePath: h = "/organizations", renderToaster: g = !1 } = f, _ =
|
|
11
|
+
let { registerNavItems: p, registerFooterItems: m, basePath: h = "/organizations", renderToaster: g = !1 } = f, _ = n(), v = (e, t) => (e.endsWith("/") ? e.slice(0, -1) : e) + (t.startsWith("/") ? t : `/${t}`);
|
|
12
12
|
return r(() => {
|
|
13
|
-
if (p) return
|
|
13
|
+
if (!p) return;
|
|
14
|
+
let e = [
|
|
14
15
|
{
|
|
15
16
|
id: "admin-org-organizations",
|
|
16
17
|
label: _("nav.organizations", "Organizations"),
|
|
@@ -43,26 +44,29 @@ var f = (f) => {
|
|
|
43
44
|
order: 60,
|
|
44
45
|
group: "globalconsole"
|
|
45
46
|
}
|
|
46
|
-
]
|
|
47
|
+
];
|
|
48
|
+
return p(e);
|
|
47
49
|
}, [
|
|
48
50
|
p,
|
|
49
51
|
h,
|
|
50
52
|
_
|
|
51
53
|
]), r(() => {
|
|
52
|
-
if (m) return
|
|
54
|
+
if (!m) return;
|
|
55
|
+
let e = [{
|
|
53
56
|
id: "organization-management-version",
|
|
54
57
|
label: _("footer.adminVersion", "Organization Management v0.1.0"),
|
|
55
58
|
type: "status",
|
|
56
59
|
order: 30,
|
|
57
60
|
section: "left"
|
|
58
|
-
}]
|
|
61
|
+
}];
|
|
62
|
+
return m(e);
|
|
59
63
|
}, [m, _]), /* @__PURE__ */ u(l, {
|
|
60
64
|
workspaceId: null,
|
|
61
65
|
gateway: "global",
|
|
62
66
|
defaultEnabled: !0,
|
|
63
67
|
children: /* @__PURE__ */ d(e, {
|
|
64
68
|
...f,
|
|
65
|
-
children: [/* @__PURE__ */ u(
|
|
69
|
+
children: [/* @__PURE__ */ u(t, {}), g && /* @__PURE__ */ u(c, {
|
|
66
70
|
position: "top-right",
|
|
67
71
|
richColors: !0,
|
|
68
72
|
closeButton: !0
|
|
@@ -70,7 +74,6 @@ var f = (f) => {
|
|
|
70
74
|
})
|
|
71
75
|
});
|
|
72
76
|
};
|
|
73
|
-
//#endregion
|
|
74
77
|
export { f as AdminOrganizationRoot };
|
|
75
78
|
|
|
76
79
|
//# sourceMappingURL=AdminOrganizationRoot.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminOrganizationRoot.js","names":[],"sources":["../../src/organization-management/AdminOrganizationRoot.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useEffect } from 'react';\nimport { Building, Briefcase, Mail, Users } from 'lucide-react';\nimport { Toaster } from '@burdenoff/fe-libs/ui';\nimport { FeatureFlagProvider } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { OrgConsoleProvider } from './providers/OrgConsoleProvider';\nimport type { NavItem, OrganizationRootProps } from './types';\nimport { AdminOrganizationRoutes } from './AdminOrganizationRoutes';\nimport { useTr } from './shared/hooks/useTr';\n\n/**\n * AdminOrganizationRoot\n *\n * Full-capability root for the organization management MFE, intended to\n * be mounted inside the admin application. Exposes organizations,\n * workspaces, members, and invites management — registering nav items\n * for each top-level surface with IDs prefixed `admin-org-*`.\n *\n * Takes the same props as `OrganizationRootProps` so hosts can\n * swap between the user and admin roots without adapter code.\n */\nexport const AdminOrganizationRoot: FC<OrganizationRootProps> = (props) => {\n const {\n registerNavItems,\n registerFooterItems,\n basePath = '/organizations',\n renderToaster = false,\n } = props;\n const tr = useTr();\n\n const joinPath = (base: string, path: string): string => {\n const cleanBase = base.endsWith('/') ? base.slice(0, -1) : base;\n const cleanPath = path.startsWith('/') ? path : `/${path}`;\n return cleanBase + cleanPath;\n };\n\n useEffect(() => {\n if (!registerNavItems) return;\n\n const navItems: NavItem[] = [\n {\n id: 'admin-org-organizations',\n label: tr('nav.organizations', 'Organizations'),\n path: basePath,\n icon: <Building size={20} />,\n order: 30,\n group: 'globalconsole',\n },\n {\n id: 'admin-org-workspaces',\n label: tr('nav.workspaces', 'Workspaces'),\n path: joinPath(basePath, '/workspaces'),\n icon: <Briefcase size={20} />,\n order: 40,\n group: 'globalconsole',\n },\n {\n id: 'admin-org-members',\n label: tr('nav.members', 'Members'),\n // Members are accessed through org detail pages; point at orgs\n // list so admins have a discoverable entry point in the\n // top-level nav.\n path: basePath,\n icon: <Users size={20} />,\n order: 50,\n group: 'globalconsole',\n },\n {\n id: 'admin-org-invites',\n label: tr('nav.myInvites', 'My Invites'),\n path: joinPath(basePath, '/invites'),\n icon: <Mail size={20} />,\n order: 60,\n group: 'globalconsole',\n },\n ];\n\n const cleanup = registerNavItems(navItems);\n return cleanup;\n }, [registerNavItems, basePath, tr]);\n\n useEffect(() => {\n if (!registerFooterItems) return;\n\n const footerItems = [\n {\n id: 'organization-management-version',\n label: tr('footer.adminVersion', 'Organization Management v0.1.0'),\n type: 'status' as const,\n order: 30,\n section: 'left' as const,\n },\n ];\n\n const cleanup = registerFooterItems(footerItems);\n return cleanup;\n }, [registerFooterItems, tr]);\n\n return (\n <FeatureFlagProvider workspaceId={null} gateway=\"global\" defaultEnabled={true}>\n <OrgConsoleProvider {...props}>\n <AdminOrganizationRoutes />\n {renderToaster && <Toaster position=\"top-right\" richColors closeButton />}\n </OrgConsoleProvider>\n </FeatureFlagProvider>\n );\n};\n"],"mappings":";;;;;;;;;AAqBA,
|
|
1
|
+
{"version":3,"file":"AdminOrganizationRoot.js","names":["AdminOrganizationRoot","props","tr","useTr","joinPath","base","path","useEffect","registerNavItems","navItems","basePath","Building","Briefcase","Users","Mail","registerFooterItems","footerItems","FeatureFlagProvider","OrgConsoleProvider","AdminOrganizationRoutes","renderToaster","Toaster"],"sources":["../../src/organization-management/AdminOrganizationRoot.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useEffect } from 'react';\nimport { Building, Briefcase, Mail, Users } from 'lucide-react';\nimport { Toaster } from '@burdenoff/fe-libs/ui';\nimport { FeatureFlagProvider } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { OrgConsoleProvider } from './providers/OrgConsoleProvider';\nimport type { NavItem, OrganizationRootProps } from './types';\nimport { AdminOrganizationRoutes } from './AdminOrganizationRoutes';\nimport { useTr } from './shared/hooks/useTr';\n\n/**\n * AdminOrganizationRoot\n *\n * Full-capability root for the organization management MFE, intended to\n * be mounted inside the admin application. Exposes organizations,\n * workspaces, members, and invites management — registering nav items\n * for each top-level surface with IDs prefixed `admin-org-*`.\n *\n * Takes the same props as `OrganizationRootProps` so hosts can\n * swap between the user and admin roots without adapter code.\n */\nexport const AdminOrganizationRoot: FC<OrganizationRootProps> = (props) => {\n const {\n registerNavItems,\n registerFooterItems,\n basePath = '/organizations',\n renderToaster = false,\n } = props;\n const tr = useTr();\n\n const joinPath = (base: string, path: string): string => {\n const cleanBase = base.endsWith('/') ? base.slice(0, -1) : base;\n const cleanPath = path.startsWith('/') ? path : `/${path}`;\n return cleanBase + cleanPath;\n };\n\n useEffect(() => {\n if (!registerNavItems) return;\n\n const navItems: NavItem[] = [\n {\n id: 'admin-org-organizations',\n label: tr('nav.organizations', 'Organizations'),\n path: basePath,\n icon: <Building size={20} />,\n order: 30,\n group: 'globalconsole',\n },\n {\n id: 'admin-org-workspaces',\n label: tr('nav.workspaces', 'Workspaces'),\n path: joinPath(basePath, '/workspaces'),\n icon: <Briefcase size={20} />,\n order: 40,\n group: 'globalconsole',\n },\n {\n id: 'admin-org-members',\n label: tr('nav.members', 'Members'),\n // Members are accessed through org detail pages; point at orgs\n // list so admins have a discoverable entry point in the\n // top-level nav.\n path: basePath,\n icon: <Users size={20} />,\n order: 50,\n group: 'globalconsole',\n },\n {\n id: 'admin-org-invites',\n label: tr('nav.myInvites', 'My Invites'),\n path: joinPath(basePath, '/invites'),\n icon: <Mail size={20} />,\n order: 60,\n group: 'globalconsole',\n },\n ];\n\n const cleanup = registerNavItems(navItems);\n return cleanup;\n }, [registerNavItems, basePath, tr]);\n\n useEffect(() => {\n if (!registerFooterItems) return;\n\n const footerItems = [\n {\n id: 'organization-management-version',\n label: tr('footer.adminVersion', 'Organization Management v0.1.0'),\n type: 'status' as const,\n order: 30,\n section: 'left' as const,\n },\n ];\n\n const cleanup = registerFooterItems(footerItems);\n return cleanup;\n }, [registerFooterItems, tr]);\n\n return (\n <FeatureFlagProvider workspaceId={null} gateway=\"global\" defaultEnabled={true}>\n <OrgConsoleProvider {...props}>\n <AdminOrganizationRoutes />\n {renderToaster && <Toaster position=\"top-right\" richColors closeButton />}\n </OrgConsoleProvider>\n </FeatureFlagProvider>\n );\n};\n"],"mappings":";;;;;;;;;AAqBA,IAAaA,KAAoD,MAAU;CACzE,IAAM,EACJ,qBACA,wBACA,cAAW,kBACX,mBAAgB,OACdC,GACEC,IAAKC,EAAM,GAEXC,KAAY,GAAc,OACZC,EAAK,SAAS,GAAG,IAAIA,EAAK,MAAM,GAAG,EAAE,IAAIA,MACzCC,EAAK,WAAW,GAAG,IAAIA,IAAO,IAAIA;CAkEtD,OA9DAC,QAAgB;EACd,IAAI,CAACC,GAAkB;EAEvB,IAAMC,IAAsB;GAC1B;IACE,IAAI;IACJ,OAAOP,EAAG,qBAAqB,eAAe;IAC9C,MAAMQ;IACN,MAAM,kBAACC,GAAD,EAAU,MAAM,GAAK,CAAA;IAC3B,OAAO;IACP,OAAO;GACT;GACA;IACE,IAAI;IACJ,OAAOT,EAAG,kBAAkB,YAAY;IACxC,MAAME,EAASM,GAAU,aAAa;IACtC,MAAM,kBAACE,GAAD,EAAW,MAAM,GAAK,CAAA;IAC5B,OAAO;IACP,OAAO;GACT;GACA;IACE,IAAI;IACJ,OAAOV,EAAG,eAAe,SAAS;IAIlC,MAAMQ;IACN,MAAM,kBAACG,GAAD,EAAO,MAAM,GAAK,CAAA;IACxB,OAAO;IACP,OAAO;GACT;GACA;IACE,IAAI;IACJ,OAAOX,EAAG,iBAAiB,YAAY;IACvC,MAAME,EAASM,GAAU,UAAU;IACnC,MAAM,kBAACI,GAAD,EAAM,MAAM,GAAK,CAAA;IACvB,OAAO;IACP,OAAO;GACT;EACF;EAGA,OADgBN,EAAiBC,CAC1B;CACT,GAAG;EAACD;EAAkBE;EAAUR;CAAE,CAAC,GAEnCK,QAAgB;EACd,IAAI,CAACQ,GAAqB;EAE1B,IAAMC,IAAc,CAClB;GACE,IAAI;GACJ,OAAOd,EAAG,uBAAuB,gCAAgC;GACjE,MAAM;GACN,OAAO;GACP,SAAS;EACX,CACF;EAGA,OADgBa,EAAoBC,CAC7B;CACT,GAAG,CAACD,GAAqBb,CAAE,CAAC,GAG1B,kBAACe,GAAD;EAAqB,aAAa;EAAM,SAAQ;EAAS,gBAAgB;EACvE,UAAA,kBAACC,GAAD;GAAoB,GAAIjB;GAAxB,UAAA,CACE,kBAACkB,GAAD,CAA0B,CAAA,GACzBC,KAAiB,kBAACC,GAAD;IAAS,UAAS;IAAY,YAAA;IAAW,aAAA;GAAa,CAAA,CACtD;;CACD,CAAA;AAEzB"}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { OrgScopedPermissions as e } from "./providers/OrgScopedPermissions.js";
|
|
2
|
+
import { OrganizationsListPage as t } from "./modules/organizations/pages/OrganizationsListPage.js";
|
|
3
3
|
import { Suspense as n, lazy as r } from "react";
|
|
4
4
|
import { useFeatureFlagDetails as i } from "@burdenoff/fe-libs/shared/feature-flags";
|
|
5
5
|
import { jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
6
6
|
import { Navigate as s, Outlet as c, Route as l, Routes as u, useParams as d } from "react-router";
|
|
7
|
-
|
|
7
|
+
// src/organization-management/AdminOrganizationRoutes.tsx
|
|
8
8
|
var f = r(() => import("./modules/organizations/pages/OrganizationCreatePage.js").then((e) => ({ default: e.OrganizationCreatePage }))), p = r(() => import("./modules/organizations/pages/OrganizationDetailPage.js").then((e) => ({ default: e.OrganizationDetailPage }))), m = r(() => import("./modules/organizations/pages/OrganizationEditPage.js").then((e) => ({ default: e.OrganizationEditPage }))), h = r(() => import("./modules/workspaces/pages/WorkspacesListPage.js").then((e) => ({ default: e.WorkspacesListPage }))), g = r(() => import("./modules/workspaces/pages/WorkspaceCreatePage.js").then((e) => ({ default: e.WorkspaceCreatePage }))), _ = r(() => import("./modules/workspaces/pages/WorkspaceDetailPage.js").then((e) => ({ default: e.WorkspaceDetailPage }))), v = r(() => import("./modules/workspaces/pages/WorkspaceEditPage.js").then((e) => ({ default: e.WorkspaceEditPage }))), y = r(() => import("./modules/members/pages/MembersListPage.js").then((e) => ({ default: e.MembersListPage }))), b = r(() => import("./modules/members/pages/MemberDetailPage.js").then((e) => ({ default: e.MemberDetailPage }))), x = r(() => import("./modules/members/pages/InviteMemberPage.js").then((e) => ({ default: e.InviteMemberPage }))), S = r(() => import("./modules/members/pages/MyInvitesPage.js").then((e) => ({ default: e.MyInvitesPage })));
|
|
9
|
-
function C({ paramName:
|
|
10
|
-
let n = d()[
|
|
11
|
-
return n ? /* @__PURE__ */ a(
|
|
9
|
+
function C({ paramName: t }) {
|
|
10
|
+
let n = d()[t];
|
|
11
|
+
return n ? /* @__PURE__ */ a(e, {
|
|
12
12
|
organizationId: n,
|
|
13
13
|
children: /* @__PURE__ */ a(c, {})
|
|
14
14
|
}) : /* @__PURE__ */ a(c, {});
|
|
@@ -20,13 +20,13 @@ function w() {
|
|
|
20
20
|
});
|
|
21
21
|
}
|
|
22
22
|
function T() {
|
|
23
|
-
let
|
|
23
|
+
let e = i("organization.create"), r = i("organization.invite"), c = !e || !!e.value, d = !r || !!r.value;
|
|
24
24
|
return /* @__PURE__ */ a(n, {
|
|
25
25
|
fallback: /* @__PURE__ */ a(w, {}),
|
|
26
26
|
children: /* @__PURE__ */ o(u, { children: [
|
|
27
27
|
/* @__PURE__ */ a(l, {
|
|
28
28
|
index: !0,
|
|
29
|
-
element: /* @__PURE__ */ a(
|
|
29
|
+
element: /* @__PURE__ */ a(t, { mode: "admin" })
|
|
30
30
|
}),
|
|
31
31
|
/* @__PURE__ */ a(l, {
|
|
32
32
|
path: "list",
|
|
@@ -95,7 +95,6 @@ function T() {
|
|
|
95
95
|
] })
|
|
96
96
|
});
|
|
97
97
|
}
|
|
98
|
-
//#endregion
|
|
99
98
|
export { T as AdminOrganizationRoutes };
|
|
100
99
|
|
|
101
100
|
//# sourceMappingURL=AdminOrganizationRoutes.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminOrganizationRoutes.js","names":[],"sources":["../../src/organization-management/AdminOrganizationRoutes.tsx"],"sourcesContent":["import { lazy, Suspense } from 'react';\nimport { Navigate, Outlet, Route, Routes, useParams } from 'react-router';\nimport { useFeatureFlagDetails } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { OrgScopedPermissions } from './providers/OrgScopedPermissions';\n\n// Eager load - main entry point page\nimport { OrganizationsListPage } from './modules/organizations/pages/OrganizationsListPage';\n\n// Lazy load - secondary pages\nconst OrganizationCreatePage = lazy(() =>\n import('./modules/organizations/pages/OrganizationCreatePage').then((m) => ({\n default: m.OrganizationCreatePage,\n }))\n);\nconst OrganizationDetailPage = lazy(() =>\n import('./modules/organizations/pages/OrganizationDetailPage').then((m) => ({\n default: m.OrganizationDetailPage,\n }))\n);\nconst OrganizationEditPage = lazy(() =>\n import('./modules/organizations/pages/OrganizationEditPage').then((m) => ({\n default: m.OrganizationEditPage,\n }))\n);\n\n// Workspaces - lazy load all\nconst WorkspacesListPage = lazy(() =>\n import('./modules/workspaces/pages/WorkspacesListPage').then((m) => ({\n default: m.WorkspacesListPage,\n }))\n);\nconst WorkspaceCreatePage = lazy(() =>\n import('./modules/workspaces/pages/WorkspaceCreatePage').then((m) => ({\n default: m.WorkspaceCreatePage,\n }))\n);\nconst WorkspaceDetailPage = lazy(() =>\n import('./modules/workspaces/pages/WorkspaceDetailPage').then((m) => ({\n default: m.WorkspaceDetailPage,\n }))\n);\nconst WorkspaceEditPage = lazy(() =>\n import('./modules/workspaces/pages/WorkspaceEditPage').then((m) => ({\n default: m.WorkspaceEditPage,\n }))\n);\n\n// Members - lazy load all\nconst MembersListPage = lazy(() =>\n import('./modules/members/pages/MembersListPage').then((m) => ({ default: m.MembersListPage }))\n);\nconst MemberDetailPage = lazy(() =>\n import('./modules/members/pages/MemberDetailPage').then((m) => ({ default: m.MemberDetailPage }))\n);\nconst InviteMemberPage = lazy(() =>\n import('./modules/members/pages/InviteMemberPage').then((m) => ({ default: m.InviteMemberPage }))\n);\nconst MyInvitesPage = lazy(() =>\n import('./modules/members/pages/MyInvitesPage').then((m) => ({ default: m.MyInvitesPage }))\n);\n\n/**\n * Layout wrapper that scopes the PermissionProvider to the current\n * organization's id. This layout owns the `:id` or `:organizationId` URL\n * segment directly (see route declarations below) so\n * `useParams()[paramName]` is guaranteed to be populated and the\n * `OrgScopedPermissions` provider always wraps its descendants. Mirrors\n * the helper in UserOrganizationRoutes — keep them aligned.\n */\nfunction OrgScopedRouteLayout({ paramName }: { paramName: 'id' | 'organizationId' }) {\n const params = useParams<Record<string, string | undefined>>();\n const orgId = params[paramName];\n if (!orgId) return <Outlet />;\n return (\n <OrgScopedPermissions organizationId={orgId}>\n <Outlet />\n </OrgScopedPermissions>\n );\n}\n\n/**\n * Loading fallback for lazy-loaded pages\n */\nfunction PageLoadingFallback() {\n return (\n <div className=\"flex items-center justify-center min-h-[200px]\">\n <div className=\"size-8 animate-spin rounded-full border-2 border-[var(--color-border-default)] border-t-[var(--color-action-primary-bg)]\" />\n </div>\n );\n}\n\n/**\n * AdminOrganizationRoutes\n *\n * Full admin routing surface for the organization management MFE,\n * mounted by the admin application. Exposes all pages:\n * organizations CRUD, workspaces CRUD, members management, and\n * user-level invites.\n *\n * Routes start from root (/) since parent route is /organizations/*\n *\n * Lazy loading strategy:\n * - OrganizationsListPage: Eager loaded (main entry point)\n * - All other pages: Lazy loaded for better initial bundle size\n */\nexport function AdminOrganizationRoutes() {\n // Keep aligned with the platform feature-flag default-allow policy: an\n // unseeded flag must not disable shipped CRUDL. Explicit false still gates a\n // surface off when admins configure the flag.\n const createDetails = useFeatureFlagDetails('organization.create');\n const inviteDetails = useFeatureFlagDetails('organization.invite');\n const createEnabled = createDetails ? Boolean(createDetails.value) : true;\n const inviteEnabled = inviteDetails ? Boolean(inviteDetails.value) : true;\n\n return (\n <Suspense fallback={<PageLoadingFallback />}>\n <Routes>\n {/* Organizations - index is the list (eager loaded). Admin app\n renders the platform-wide list (organizations query); product-app\n shells mounting this page should leave the default `mode=\"my\"`. */}\n <Route index element={<OrganizationsListPage mode=\"admin\" />} />\n {/* Keep legacy/bookmarked /organizations/list links on the list page. */}\n <Route path=\"list\" element={<Navigate to=\"..\" replace />} />\n {createEnabled && <Route path=\"new\" element={<OrganizationCreatePage />} />}\n\n {/* Per-org routes — the layout OWNS the `:id` segment so\n useParams().id is guaranteed to be set inside\n OrgScopedPermissions. */}\n <Route path=\":id\" element={<OrgScopedRouteLayout paramName=\"id\" />}>\n <Route index element={<OrganizationDetailPage />} />\n <Route path=\"edit\" element={<OrganizationEditPage />} />\n </Route>\n\n {/* Workspaces — literal \"workspaces\" prefix ranks above the bare\n dynamic `:id` / `:organizationId` segments. */}\n <Route path=\"workspaces\" element={<WorkspacesListPage mode=\"admin\" />} />\n <Route path=\"workspaces/new\" element={<WorkspaceCreatePage />} />\n <Route path=\"workspaces/:id\" element={<WorkspaceDetailPage />} />\n <Route path=\"workspaces/:id/edit\" element={<WorkspaceEditPage />} />\n\n {/* My Invites - user-level, not org-scoped. Declared before the\n `:organizationId` block so the literal \"invites\" wins the rank\n match. */}\n <Route path=\"invites\" element={<MyInvitesPage activeTab=\"workspace\" />} />\n <Route path=\"invites/organizations\" element={<MyInvitesPage activeTab=\"organization\" />} />\n\n {/* Member routes — layout OWNS the `:organizationId` segment for\n the same reason as the detail block above. */}\n <Route path=\":organizationId\" element={<OrgScopedRouteLayout paramName=\"organizationId\" />}>\n {inviteEnabled && <Route path=\"members/invite\" element={<InviteMemberPage />} />}\n <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":"AdminOrganizationRoutes.js","names":["OrganizationCreatePage","lazy","m","OrganizationDetailPage","OrganizationEditPage","WorkspacesListPage","WorkspaceCreatePage","WorkspaceDetailPage","WorkspaceEditPage","MembersListPage","MemberDetailPage","InviteMemberPage","MyInvitesPage","OrgScopedRouteLayout","orgId","useParams","paramName","OrgScopedPermissions","Outlet","PageLoadingFallback","AdminOrganizationRoutes","createDetails","useFeatureFlagDetails","inviteDetails","createEnabled","inviteEnabled","Suspense","Routes","Route","OrganizationsListPage","Navigate"],"sources":["../../src/organization-management/AdminOrganizationRoutes.tsx"],"sourcesContent":["import { lazy, Suspense } from 'react';\nimport { Navigate, Outlet, Route, Routes, useParams } from 'react-router';\nimport { useFeatureFlagDetails } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { OrgScopedPermissions } from './providers/OrgScopedPermissions';\n\n// Eager load - main entry point page\nimport { OrganizationsListPage } from './modules/organizations/pages/OrganizationsListPage';\n\n// Lazy load - secondary pages\nconst OrganizationCreatePage = lazy(() =>\n import('./modules/organizations/pages/OrganizationCreatePage').then((m) => ({\n default: m.OrganizationCreatePage,\n }))\n);\nconst OrganizationDetailPage = lazy(() =>\n import('./modules/organizations/pages/OrganizationDetailPage').then((m) => ({\n default: m.OrganizationDetailPage,\n }))\n);\nconst OrganizationEditPage = lazy(() =>\n import('./modules/organizations/pages/OrganizationEditPage').then((m) => ({\n default: m.OrganizationEditPage,\n }))\n);\n\n// Workspaces - lazy load all\nconst WorkspacesListPage = lazy(() =>\n import('./modules/workspaces/pages/WorkspacesListPage').then((m) => ({\n default: m.WorkspacesListPage,\n }))\n);\nconst WorkspaceCreatePage = lazy(() =>\n import('./modules/workspaces/pages/WorkspaceCreatePage').then((m) => ({\n default: m.WorkspaceCreatePage,\n }))\n);\nconst WorkspaceDetailPage = lazy(() =>\n import('./modules/workspaces/pages/WorkspaceDetailPage').then((m) => ({\n default: m.WorkspaceDetailPage,\n }))\n);\nconst WorkspaceEditPage = lazy(() =>\n import('./modules/workspaces/pages/WorkspaceEditPage').then((m) => ({\n default: m.WorkspaceEditPage,\n }))\n);\n\n// Members - lazy load all\nconst MembersListPage = lazy(() =>\n import('./modules/members/pages/MembersListPage').then((m) => ({ default: m.MembersListPage }))\n);\nconst MemberDetailPage = lazy(() =>\n import('./modules/members/pages/MemberDetailPage').then((m) => ({ default: m.MemberDetailPage }))\n);\nconst InviteMemberPage = lazy(() =>\n import('./modules/members/pages/InviteMemberPage').then((m) => ({ default: m.InviteMemberPage }))\n);\nconst MyInvitesPage = lazy(() =>\n import('./modules/members/pages/MyInvitesPage').then((m) => ({ default: m.MyInvitesPage }))\n);\n\n/**\n * Layout wrapper that scopes the PermissionProvider to the current\n * organization's id. This layout owns the `:id` or `:organizationId` URL\n * segment directly (see route declarations below) so\n * `useParams()[paramName]` is guaranteed to be populated and the\n * `OrgScopedPermissions` provider always wraps its descendants. Mirrors\n * the helper in UserOrganizationRoutes — keep them aligned.\n */\nfunction OrgScopedRouteLayout({ paramName }: { paramName: 'id' | 'organizationId' }) {\n const params = useParams<Record<string, string | undefined>>();\n const orgId = params[paramName];\n if (!orgId) return <Outlet />;\n return (\n <OrgScopedPermissions organizationId={orgId}>\n <Outlet />\n </OrgScopedPermissions>\n );\n}\n\n/**\n * Loading fallback for lazy-loaded pages\n */\nfunction PageLoadingFallback() {\n return (\n <div className=\"flex items-center justify-center min-h-[200px]\">\n <div className=\"size-8 animate-spin rounded-full border-2 border-[var(--color-border-default)] border-t-[var(--color-action-primary-bg)]\" />\n </div>\n );\n}\n\n/**\n * AdminOrganizationRoutes\n *\n * Full admin routing surface for the organization management MFE,\n * mounted by the admin application. Exposes all pages:\n * organizations CRUD, workspaces CRUD, members management, and\n * user-level invites.\n *\n * Routes start from root (/) since parent route is /organizations/*\n *\n * Lazy loading strategy:\n * - OrganizationsListPage: Eager loaded (main entry point)\n * - All other pages: Lazy loaded for better initial bundle size\n */\nexport function AdminOrganizationRoutes() {\n // Keep aligned with the platform feature-flag default-allow policy: an\n // unseeded flag must not disable shipped CRUDL. Explicit false still gates a\n // surface off when admins configure the flag.\n const createDetails = useFeatureFlagDetails('organization.create');\n const inviteDetails = useFeatureFlagDetails('organization.invite');\n const createEnabled = createDetails ? Boolean(createDetails.value) : true;\n const inviteEnabled = inviteDetails ? Boolean(inviteDetails.value) : true;\n\n return (\n <Suspense fallback={<PageLoadingFallback />}>\n <Routes>\n {/* Organizations - index is the list (eager loaded). Admin app\n renders the platform-wide list (organizations query); product-app\n shells mounting this page should leave the default `mode=\"my\"`. */}\n <Route index element={<OrganizationsListPage mode=\"admin\" />} />\n {/* Keep legacy/bookmarked /organizations/list links on the list page. */}\n <Route path=\"list\" element={<Navigate to=\"..\" replace />} />\n {createEnabled && <Route path=\"new\" element={<OrganizationCreatePage />} />}\n\n {/* Per-org routes — the layout OWNS the `:id` segment so\n useParams().id is guaranteed to be set inside\n OrgScopedPermissions. */}\n <Route path=\":id\" element={<OrgScopedRouteLayout paramName=\"id\" />}>\n <Route index element={<OrganizationDetailPage />} />\n <Route path=\"edit\" element={<OrganizationEditPage />} />\n </Route>\n\n {/* Workspaces — literal \"workspaces\" prefix ranks above the bare\n dynamic `:id` / `:organizationId` segments. */}\n <Route path=\"workspaces\" element={<WorkspacesListPage mode=\"admin\" />} />\n <Route path=\"workspaces/new\" element={<WorkspaceCreatePage />} />\n <Route path=\"workspaces/:id\" element={<WorkspaceDetailPage />} />\n <Route path=\"workspaces/:id/edit\" element={<WorkspaceEditPage />} />\n\n {/* My Invites - user-level, not org-scoped. Declared before the\n `:organizationId` block so the literal \"invites\" wins the rank\n match. */}\n <Route path=\"invites\" element={<MyInvitesPage activeTab=\"workspace\" />} />\n <Route path=\"invites/organizations\" element={<MyInvitesPage activeTab=\"organization\" />} />\n\n {/* Member routes — layout OWNS the `:organizationId` segment for\n the same reason as the detail block above. */}\n <Route path=\":organizationId\" element={<OrgScopedRouteLayout paramName=\"organizationId\" />}>\n {inviteEnabled && <Route path=\"members/invite\" element={<InviteMemberPage />} />}\n <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,16 +1,17 @@
|
|
|
1
1
|
import { OrgConsoleProvider as e } from "./providers/OrgConsoleProvider.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { UserOrganizationRoutes as t } from "./UserOrganizationRoutes.js";
|
|
3
|
+
import { useTr as n } from "./shared/hooks/useTr.js";
|
|
4
4
|
import { useEffect as r } from "react";
|
|
5
5
|
import { Building2 as i, Mail as a, UserPlus as o } from "lucide-react";
|
|
6
6
|
import { Toaster as s } from "@burdenoff/fe-libs/ui";
|
|
7
7
|
import { FeatureFlagProvider as c } from "@burdenoff/fe-libs/shared/feature-flags";
|
|
8
8
|
import { jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
9
|
-
|
|
9
|
+
// src/organization-management/UserOrganizationRoot.tsx
|
|
10
10
|
var d = (d) => {
|
|
11
|
-
let { registerNavItems: f, registerFooterItems: p, basePath: m = "/organizations", renderToaster: h = !1 } = d, g =
|
|
11
|
+
let { registerNavItems: f, registerFooterItems: p, basePath: m = "/organizations", renderToaster: h = !1 } = d, g = n(), _ = (e, t) => (e.endsWith("/") ? e.slice(0, -1) : e) + (t.startsWith("/") ? t : `/${t}`);
|
|
12
12
|
return r(() => {
|
|
13
|
-
if (f) return
|
|
13
|
+
if (!f) return;
|
|
14
|
+
let e = [
|
|
14
15
|
{
|
|
15
16
|
id: "user-org-organizations",
|
|
16
17
|
label: g("nav.organizations", "Organizations"),
|
|
@@ -35,26 +36,29 @@ var d = (d) => {
|
|
|
35
36
|
order: 30,
|
|
36
37
|
group: "user"
|
|
37
38
|
}
|
|
38
|
-
]
|
|
39
|
+
];
|
|
40
|
+
return f(e);
|
|
39
41
|
}, [
|
|
40
42
|
f,
|
|
41
43
|
m,
|
|
42
44
|
g
|
|
43
45
|
]), r(() => {
|
|
44
|
-
if (p) return
|
|
46
|
+
if (!p) return;
|
|
47
|
+
let e = [{
|
|
45
48
|
id: "user-organization-version",
|
|
46
49
|
label: g("footer.userVersion", "Organization (User) v0.1.0"),
|
|
47
50
|
type: "status",
|
|
48
51
|
order: 30,
|
|
49
52
|
section: "left"
|
|
50
|
-
}]
|
|
53
|
+
}];
|
|
54
|
+
return p(e);
|
|
51
55
|
}, [p, g]), /* @__PURE__ */ l(c, {
|
|
52
56
|
workspaceId: null,
|
|
53
57
|
gateway: "global",
|
|
54
58
|
defaultEnabled: !0,
|
|
55
59
|
children: /* @__PURE__ */ u(e, {
|
|
56
60
|
...d,
|
|
57
|
-
children: [/* @__PURE__ */ l(
|
|
61
|
+
children: [/* @__PURE__ */ l(t, {}), h && /* @__PURE__ */ l(s, {
|
|
58
62
|
position: "top-right",
|
|
59
63
|
richColors: !0,
|
|
60
64
|
closeButton: !0
|
|
@@ -62,7 +66,6 @@ var d = (d) => {
|
|
|
62
66
|
})
|
|
63
67
|
});
|
|
64
68
|
};
|
|
65
|
-
//#endregion
|
|
66
69
|
export { d as UserOrganizationRoot };
|
|
67
70
|
|
|
68
71
|
//# sourceMappingURL=UserOrganizationRoot.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UserOrganizationRoot.js","names":[],"sources":["../../src/organization-management/UserOrganizationRoot.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useEffect } from 'react';\nimport { Building2, Mail, UserPlus } from 'lucide-react';\nimport { Toaster } from '@burdenoff/fe-libs/ui';\nimport { FeatureFlagProvider } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { OrgConsoleProvider } from './providers/OrgConsoleProvider';\nimport type { NavItem, OrganizationRootProps } from './types';\nimport { UserOrganizationRoutes } from './UserOrganizationRoutes';\nimport { useTr } from './shared/hooks/useTr';\n\n/**\n * UserOrganizationRoot\n *\n * User-facing root for the organization management MFE, intended to be\n * mounted inside product shells (workspaces, botlit, fluidgrids, etc.).\n *\n * Exposes the membership-scoped organization surface:\n * - Organizations list (`mode=\"my\"` → `myOrganizations`)\n * - Organization detail / edit (per-action `hasPermission` gates;\n * backend enforces membership on `organization(id)` resolver)\n * - Members list / detail / invite (backend enforces membership on\n * `organizationMembers` resolver)\n * - My pending invites\n *\n * Registers \"Organizations\" + \"My Invites\" nav items.\n *\n * Takes the same props as `OrganizationRootProps` so hosts can swap\n * between the user and admin roots without adapter code.\n */\nexport const UserOrganizationRoot: FC<OrganizationRootProps> = (props) => {\n const {\n registerNavItems,\n registerFooterItems,\n basePath = '/organizations',\n renderToaster = false,\n } = props;\n const tr = useTr();\n\n const joinPath = (base: string, path: string): string => {\n const cleanBase = base.endsWith('/') ? base.slice(0, -1) : base;\n const cleanPath = path.startsWith('/') ? path : `/${path}`;\n return cleanBase + cleanPath;\n };\n\n useEffect(() => {\n if (!registerNavItems) return;\n\n const navItems: NavItem[] = [\n {\n id: 'user-org-organizations',\n label: tr('nav.organizations', 'Organizations'),\n path: basePath,\n icon: <Building2 size={20} />,\n order: 20,\n group: 'user',\n },\n {\n id: 'user-org-invite-people',\n label: tr('nav.invitePeople', 'Invite People'),\n path: joinPath(basePath, '/invite'),\n icon: <UserPlus size={20} />,\n order: 25,\n group: 'user',\n },\n {\n id: 'user-org-invites',\n label: tr('nav.myInvites', 'My Invites'),\n path: joinPath(basePath, '/invites'),\n icon: <Mail size={20} />,\n order: 30,\n group: 'user',\n },\n ];\n\n const cleanup = registerNavItems(navItems);\n return cleanup;\n }, [registerNavItems, basePath, tr]);\n\n useEffect(() => {\n if (!registerFooterItems) return;\n\n const footerItems = [\n {\n id: 'user-organization-version',\n label: tr('footer.userVersion', 'Organization (User) v0.1.0'),\n type: 'status' as const,\n order: 30,\n section: 'left' as const,\n },\n ];\n\n const cleanup = registerFooterItems(footerItems);\n return cleanup;\n }, [registerFooterItems, tr]);\n\n return (\n <FeatureFlagProvider workspaceId={null} gateway=\"global\" defaultEnabled={true}>\n <OrgConsoleProvider {...props}>\n <UserOrganizationRoutes />\n {renderToaster && <Toaster position=\"top-right\" richColors closeButton />}\n </OrgConsoleProvider>\n </FeatureFlagProvider>\n );\n};\n"],"mappings":";;;;;;;;;AA6BA,
|
|
1
|
+
{"version":3,"file":"UserOrganizationRoot.js","names":["UserOrganizationRoot","props","tr","useTr","joinPath","base","path","useEffect","registerNavItems","navItems","basePath","Building2","UserPlus","Mail","registerFooterItems","footerItems","FeatureFlagProvider","OrgConsoleProvider","UserOrganizationRoutes","renderToaster","Toaster"],"sources":["../../src/organization-management/UserOrganizationRoot.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useEffect } from 'react';\nimport { Building2, Mail, UserPlus } from 'lucide-react';\nimport { Toaster } from '@burdenoff/fe-libs/ui';\nimport { FeatureFlagProvider } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { OrgConsoleProvider } from './providers/OrgConsoleProvider';\nimport type { NavItem, OrganizationRootProps } from './types';\nimport { UserOrganizationRoutes } from './UserOrganizationRoutes';\nimport { useTr } from './shared/hooks/useTr';\n\n/**\n * UserOrganizationRoot\n *\n * User-facing root for the organization management MFE, intended to be\n * mounted inside product shells (workspaces, botlit, fluidgrids, etc.).\n *\n * Exposes the membership-scoped organization surface:\n * - Organizations list (`mode=\"my\"` → `myOrganizations`)\n * - Organization detail / edit (per-action `hasPermission` gates;\n * backend enforces membership on `organization(id)` resolver)\n * - Members list / detail / invite (backend enforces membership on\n * `organizationMembers` resolver)\n * - My pending invites\n *\n * Registers \"Organizations\" + \"My Invites\" nav items.\n *\n * Takes the same props as `OrganizationRootProps` so hosts can swap\n * between the user and admin roots without adapter code.\n */\nexport const UserOrganizationRoot: FC<OrganizationRootProps> = (props) => {\n const {\n registerNavItems,\n registerFooterItems,\n basePath = '/organizations',\n renderToaster = false,\n } = props;\n const tr = useTr();\n\n const joinPath = (base: string, path: string): string => {\n const cleanBase = base.endsWith('/') ? base.slice(0, -1) : base;\n const cleanPath = path.startsWith('/') ? path : `/${path}`;\n return cleanBase + cleanPath;\n };\n\n useEffect(() => {\n if (!registerNavItems) return;\n\n const navItems: NavItem[] = [\n {\n id: 'user-org-organizations',\n label: tr('nav.organizations', 'Organizations'),\n path: basePath,\n icon: <Building2 size={20} />,\n order: 20,\n group: 'user',\n },\n {\n id: 'user-org-invite-people',\n label: tr('nav.invitePeople', 'Invite People'),\n path: joinPath(basePath, '/invite'),\n icon: <UserPlus size={20} />,\n order: 25,\n group: 'user',\n },\n {\n id: 'user-org-invites',\n label: tr('nav.myInvites', 'My Invites'),\n path: joinPath(basePath, '/invites'),\n icon: <Mail size={20} />,\n order: 30,\n group: 'user',\n },\n ];\n\n const cleanup = registerNavItems(navItems);\n return cleanup;\n }, [registerNavItems, basePath, tr]);\n\n useEffect(() => {\n if (!registerFooterItems) return;\n\n const footerItems = [\n {\n id: 'user-organization-version',\n label: tr('footer.userVersion', 'Organization (User) v0.1.0'),\n type: 'status' as const,\n order: 30,\n section: 'left' as const,\n },\n ];\n\n const cleanup = registerFooterItems(footerItems);\n return cleanup;\n }, [registerFooterItems, tr]);\n\n return (\n <FeatureFlagProvider workspaceId={null} gateway=\"global\" defaultEnabled={true}>\n <OrgConsoleProvider {...props}>\n <UserOrganizationRoutes />\n {renderToaster && <Toaster position=\"top-right\" richColors closeButton />}\n </OrgConsoleProvider>\n </FeatureFlagProvider>\n );\n};\n"],"mappings":";;;;;;;;;AA6BA,IAAaA,KAAmD,MAAU;CACxE,IAAM,EACJ,qBACA,wBACA,cAAW,kBACX,mBAAgB,OACdC,GACEC,IAAKC,EAAM,GAEXC,KAAY,GAAc,OACZC,EAAK,SAAS,GAAG,IAAIA,EAAK,MAAM,GAAG,EAAE,IAAIA,MACzCC,EAAK,WAAW,GAAG,IAAIA,IAAO,IAAIA;CAuDtD,OAnDAC,QAAgB;EACd,IAAI,CAACC,GAAkB;EAEvB,IAAMC,IAAsB;GAC1B;IACE,IAAI;IACJ,OAAOP,EAAG,qBAAqB,eAAe;IAC9C,MAAMQ;IACN,MAAM,kBAACC,GAAD,EAAW,MAAM,GAAK,CAAA;IAC5B,OAAO;IACP,OAAO;GACT;GACA;IACE,IAAI;IACJ,OAAOT,EAAG,oBAAoB,eAAe;IAC7C,MAAME,EAASM,GAAU,SAAS;IAClC,MAAM,kBAACE,GAAD,EAAU,MAAM,GAAK,CAAA;IAC3B,OAAO;IACP,OAAO;GACT;GACA;IACE,IAAI;IACJ,OAAOV,EAAG,iBAAiB,YAAY;IACvC,MAAME,EAASM,GAAU,UAAU;IACnC,MAAM,kBAACG,GAAD,EAAM,MAAM,GAAK,CAAA;IACvB,OAAO;IACP,OAAO;GACT;EACF;EAGA,OADgBL,EAAiBC,CAC1B;CACT,GAAG;EAACD;EAAkBE;EAAUR;CAAE,CAAC,GAEnCK,QAAgB;EACd,IAAI,CAACO,GAAqB;EAE1B,IAAMC,IAAc,CAClB;GACE,IAAI;GACJ,OAAOb,EAAG,sBAAsB,4BAA4B;GAC5D,MAAM;GACN,OAAO;GACP,SAAS;EACX,CACF;EAGA,OADgBY,EAAoBC,CAC7B;CACT,GAAG,CAACD,GAAqBZ,CAAE,CAAC,GAG1B,kBAACc,GAAD;EAAqB,aAAa;EAAM,SAAQ;EAAS,gBAAgB;EACvE,UAAA,kBAACC,GAAD;GAAoB,GAAIhB;GAAxB,UAAA,CACE,kBAACiB,GAAD,CAAyB,CAAA,GACxBC,KAAiB,kBAACC,GAAD;IAAS,UAAS;IAAY,YAAA;IAAW,aAAA;GAAa,CAAA,CACtD;;CACD,CAAA;AAEzB"}
|
|
@@ -3,7 +3,7 @@ import { OrgScopedPermissions as t } from "./providers/OrgScopedPermissions.js";
|
|
|
3
3
|
import { Suspense as n, lazy as r } from "react";
|
|
4
4
|
import { jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
5
5
|
import { Navigate as o, Outlet as s, Route as c, Routes as l, useParams as u } from "react-router";
|
|
6
|
-
|
|
6
|
+
// src/organization-management/UserOrganizationRoutes.tsx
|
|
7
7
|
var d = r(() => import("./modules/organizations/pages/OrganizationCreatePage.js").then((e) => ({ default: e.OrganizationCreatePage }))), f = r(() => import("./modules/organizations/pages/OrganizationDetailPage.js").then((e) => ({ default: e.OrganizationDetailPage }))), p = r(() => import("./modules/organizations/pages/OrganizationEditPage.js").then((e) => ({ default: e.OrganizationEditPage }))), m = r(() => import("./modules/workspaces/pages/WorkspacesListPage.js").then((e) => ({ default: e.WorkspacesListPage }))), h = r(() => import("./modules/workspaces/pages/WorkspaceCreatePage.js").then((e) => ({ default: e.WorkspaceCreatePage }))), g = r(() => import("./modules/workspaces/pages/WorkspaceDetailPage.js").then((e) => ({ default: e.WorkspaceDetailPage }))), _ = r(() => import("./modules/workspaces/pages/WorkspaceEditPage.js").then((e) => ({ default: e.WorkspaceEditPage }))), v = r(() => import("./modules/members/pages/MembersListPage.js").then((e) => ({ default: e.MembersListPage }))), y = r(() => import("./modules/members/pages/MemberDetailPage.js").then((e) => ({ default: e.MemberDetailPage }))), b = r(() => import("./modules/members/pages/InviteMemberPage.js").then((e) => ({ default: e.InviteMemberPage }))), x = r(() => import("./modules/members/pages/MyInvitesPage.js").then((e) => ({ default: e.MyInvitesPage }))), S = r(() => import("./modules/members/pages/OrganizationInviteAcceptancePage.js").then((e) => ({ default: e.OrganizationInviteAcceptancePage }))), C = r(() => import("./modules/members/pages/InvitePeoplePage.js").then((e) => ({ default: e.InvitePeoplePage })));
|
|
8
8
|
function w({ paramName: e }) {
|
|
9
9
|
let n = u()[e];
|
|
@@ -101,7 +101,6 @@ function E() {
|
|
|
101
101
|
] })
|
|
102
102
|
});
|
|
103
103
|
}
|
|
104
|
-
//#endregion
|
|
105
104
|
export { E as UserOrganizationRoutes };
|
|
106
105
|
|
|
107
106
|
//# sourceMappingURL=UserOrganizationRoutes.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UserOrganizationRoutes.js","names":[],"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
|
+
{"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
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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":";;;;;;;;AAuCA,SAAgB,EAAe,EAC7B,mBACA,iBACA,aACA,cACsB;CACtB,IAAM,IAAK,GAAO,EACZ,IAAe,EAAyB,KAAK,EAC7C,CAAC,GAAM,KAAW,EAAuB,EAAE,CAAC,EAC5C,CAAC,GAAU,KAAe,EAAwB,KAAK,EACvD,CAAC,GAAc,KAAmB,EAAS,GAAM,EACjD,CAAC,GAAY,KAAiB,EAAS,GAAM,EAE7C,IAAa,GAChB,MAAe;EACd,IAAM,IAAS,IAAI,YAAY;AAU/B,EATA,EAAO,eAAe;AAIpB,GADA,EADe,EADF,OAAO,EAAO,UAAW,WAAW,EAAO,SAAS,IACzB,EAAG,CAC5B,KAAK,OAAO;IAAE,GAAG;IAAG,QAAQ,EAAE,MAAM,QAAQ;IAAE,EAAE,CAAC,EAChE,EAAY,EAAK,KAAK;KAExB,EAAO,gBAAgB;AACrB,KAAM,MAAM,EAAG,gCAAgC,2BAA2B,CAAC;KAE7E,EAAO,WAAW,EAAK;IAEzB,CAAC,EAAG,CACL,EAEK,KAAgB,MAA2C;EAC/D,IAAM,IAAO,EAAE,OAAO,QAAQ;AAG9B,EAFI,KAAM,EAAW,EAAK,EAE1B,EAAE,OAAO,QAAQ;IAGb,KAAU,MAAuC;AAErD,EADA,EAAE,gBAAgB,EAClB,EAAc,GAAM;EACpB,IAAM,IAAO,EAAE,aAAa,QAAQ;AACpC,EAAI,KAAM,EAAW,EAAK;IAGtB,IAAU,SAMP;EAAE,OALK,EAAK;EAKH,YAJG,EAAK,QAAQ,MAAM,EAAE,OAAO,SAAS,EAAE,CAAC;EAI/B,UAHX,EAAK,QAAQ,MAAM,EAAE,OAAO,WAAW,EAAE,CAAC;EAGrB,SAFtB,EAAK,QAAQ,MAAM,EAAE,OAAO,SAAS,UAAU,CAAC;EAEjB,QADhC,EAAK,QAAQ,MAAM,EAAE,OAAO,SAAS,SAAS,CAAC;EACP,GACtD,CAAC,EAAK,CAAC,EAEJ,IAAa,YAAY;AAC7B,MAAI,EAAQ,aAAa,KAAK,EAAc;AAC5C,IAAgB,GAAK;EAErB,IAAM,IAAkB,EACrB,KAAK,GAAG,OAAS;GAAE;GAAG;GAAK,EAAE,CAC7B,QAAQ,EAAE,WAAQ,EAAE,OAAO,WAAW,KAAK,EAAE,OAAO,SAAS,UAAU;AAE1E,OAAK,IAAM,EAAE,YAAS,GAAiB;AACrC,MAAS,MACP,EAAK,KAAK,GAAK,MAAO,MAAM,IAAM;IAAE,GAAG;IAAK,QAAQ,EAAE,MAAM,WAAW;IAAE,GAAG,EAAK,CAClF;GACD,IAAM,IAAM,EAAK;AACjB,OAAI;AAMF,IALA,MAAM,EAAa;KACjB;KACA,OAAO,EAAI;KACX,SAAS,EAAI;KACd,CAAC,EACF,GAAS,MACP,EAAK,KAAK,GAAG,MAAO,MAAM,IAAM;KAAE,GAAG;KAAG,QAAQ,EAAE,MAAM,WAAW;KAAE,GAAG,EAAG,CAC5E;YACM,GAAK;IACZ,IAAM,IAAU,EAAkB,GAAK,EAAG;AAC1C,OAAS,MACP,EAAK,KAAK,GAAG,MAAO,MAAM,IAAM;KAAE,GAAG;KAAG,QAAQ;MAAE,MAAM;MAAU;MAAS;KAAE,GAAG,EAAG,CACpF;;;AAKL,EADA,EAAgB,GAAM,EACtB,GAAS,MAAS;GAChB,IAAM,IAAU,EAAK,QAAQ,MAAM,EAAE,OAAO,SAAS,UAAU,CAAC,QAC1D,IAAS,EAAK,QAAQ,MAAM,EAAE,OAAO,SAAS,SAAS,CAAC;AAI9D,UAHA,EAAM,QACJ,GAAG,EAAG,qCAAqC,UAAU,CAAC,GAAG,EAAQ,GAAG,EAAG,gCAAgC,KAAK,CAAC,GAAG,EAAK,OAAO,IAAI,EAAO,GAAG,EAAG,oCAAoC,SAAS,CAAC,GAC5L,EACM;IACP;IAGE,IAAU,EAAK,SAAS,GACxB,IAAY,EAAQ,WAAW,KAAK,CAAC,KAAgB,CAAC,GAEtD,KAAmB,MACA,EAAI,OAAO,SAAS,IAGvC,kBAAC,QAAD;EAAM,WAAU;YAAhB,CACE,kBAAC,GAAD;GAAS,MAAM;GAAI,eAAY;GAAS,CAAA,EACvC,EAAI,OAAO,KAAK,IAAI,CAChB;MAGP,EAAI,OAAO,SAAS,YAEpB,kBAAC,QAAD;EAAM,WAAU;YAAhB,CACE,kBAAC,GAAD;GAAS,MAAM;GAAI,WAAU;GAAe,eAAY;GAAS,CAAA,EAChE,EAAG,oCAAoC,aAAa,CAChD;MAGP,EAAI,OAAO,SAAS,YAEpB,kBAAC,QAAD;EAAM,WAAU;YAAhB,CACE,kBAAC,GAAD;GAAc,MAAM;GAAI,eAAY;GAAS,CAAA,EAC5C,EAAG,oCAAoC,UAAU,CAC7C;MAGP,EAAI,OAAO,SAAS,WAEpB,kBAAC,QAAD;EAAM,WAAU;YAAhB,CACE,kBAAC,GAAD;GAAS,MAAM;GAAI,eAAY;GAAS,CAAA,EACvC,EAAI,OAAO,QACP;MAIT,kBAAC,QAAD;EAAM,WAAU;YACb,EAAG,kCAAkC,QAAQ;EACzC,CAAA,EAIL,IAAsD;EAC1D;GACE,KAAK;GACL,QAAQ;GACR,UAAU;GACV,OAAO,MACL,kBAAC,QAAD;IAAM,WAAU;cACb,EAAI;IACA,CAAA;GAEV;EACD;GACE,KAAK;GACL,QAAQ,EAAG,+BAA+B,QAAQ;GAClD,UAAU;GACV,OAAO,MACL,EAAI,SACF,kBAAC,QAAD;IAAM,WAAU;cACb,EAAG,4BAA4B,UAAU;IACrC,CAAA;GAEZ;EACD;GACE,KAAK;GACL,QAAQ,EAAG,8BAA8B,OAAO;GAChD,UAAU;GACV,OAAO,MACL,EAAI,QACF,kBAAC,QAAD;IAAM,WAAU;cACb,EAAG,uBAAuB,SAAS;IAC/B,CAAA;GAEZ;EACD;GACE,KAAK;GACL,QAAQ,EAAG,gCAAgC,SAAS;GACpD,UAAU;GACV,OAAO,MAAQ,EAAgB,EAAI;GACpC;EACF;AAED,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,KAAD;KAAG,WAAU;eACV,EACC,kCACA,yFACD;KACC,CAAA,EACJ,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,WAAU;eAHZ,CAKE,kBAAC,GAAD;MAAU,MAAM;MAAI,eAAY;MAAS,CAAA,EACxC,EAAG,uCAAuC,oBAAoB,CACxD;OACL;;GAEN,kBAAC,OAAD;IACE,aAAa,MAAM;AAEjB,KADA,EAAE,gBAAgB,EAClB,EAAc,GAAK;;IAErB,mBAAmB,EAAc,GAAM;IAC/B;IACR,WAAW,2GACT,IACI,mFACA;cAVR;KAaE,kBAAC,GAAD;MAAQ,MAAM;MAAI,WAAU;MAAqC,eAAY;MAAS,CAAA;KACtF,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,KAAD;OAAG,WAAU;iBACV,KAEG,EAAG,+BAA+B,0CAA0C;OAC9E,CAAA,EACJ,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAG,8BAA8B,6CAA6C;OAC7E,CAAA,CACA;;KACN,kBAAC,SAAD;MACE,KAAK;MACL,MAAK;MACL,QAAO;MACP,UAAU;MACV,WAAU;MACV,cAAY,EAAG,qCAAqC,kBAAkB;MACtE,CAAA;KACF,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAa,SAAS,OAAO;MAC5C,WAAU;gBAET,EAAG,iCAAiC,cAAc;MAC5C,CAAA;KACL;;GAEL,KACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,QAAD,EAAA,UAAA;MACG,EAAG,iCAAiC,aAAa;MAAC;MAAG,EAAQ;MACzD,EAAA,CAAA;KACP,kBAAC,QAAD,EAAA,UAAA;MACG,EAAG,iCAAiC,QAAQ;MAAC;MAAG,EAAQ;MACpD,EAAA,CAAA;KACN,EAAQ,aAAa,KACpB,kBAAC,QAAD;MAAM,WAAU;gBAAhB;OACG,EAAG,kCAAkC,cAAc;OAAC;OAAG,EAAQ;OAC3D;;KAER,EAAQ,UAAU,KACjB,kBAAC,QAAD;MAAM,WAAU;gBAAhB;OACG,EAAG,mCAAmC,UAAU;OAAC;OAAG,EAAQ;OACxD;;KAER,EAAQ,SAAS,KAChB,kBAAC,QAAD;MAAM,WAAU;gBAAhB;OACG,EAAG,kCAAkC,SAAS;OAAC;OAAG,EAAQ;OACtD;;KAEL;OAEN,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KACE,SAAS;KACT,MAAM;KACN,SAAS,MAAQ,GAAG,EAAI,UAAU,GAAG,EAAI;KACzC,CAAA;IACE,CAAA,CACL,EAAA,CAAA;GAGL,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,UAAU,CAAC;KACX,WAAU;eAJZ;MAMG,IACC,kBAAC,GAAD;OAAS,MAAM;OAAI,WAAU;OAAe,eAAY;OAAS,CAAA,GAEjE,kBAAC,GAAD;OAAM,MAAM;OAAI,eAAY;OAAS,CAAA;MAEtC,EAAG,2BAA2B,OAAO;MAAC;MAAE,EAAQ;MAAU;MAC1D,EAAG,iCAAiC,UAAU;MACxC;QACT,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,UAAU;KACV,WAAU;eAET,EAAG,iBAAiB,SAAS;KACvB,CAAA,CACL;;GACF"}
|
|
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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"}
|