@burdenoff/microfe-organization 2026.527.3 → 2026.527.4
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/organization-management/UserOrganizationRoot.js +39 -28
- package/dist/organization-management/UserOrganizationRoot.js.map +1 -1
- package/dist/organization-management/UserOrganizationRoutes.js +12 -8
- package/dist/organization-management/UserOrganizationRoutes.js.map +1 -1
- package/dist/organization-management/modules/members/pages/InvitePeoplePage.js +95 -0
- package/dist/organization-management/modules/members/pages/InvitePeoplePage.js.map +1 -0
- package/dist/organization-management/modules/organizations/pages/OrganizationDetailPage.js +163 -151
- package/dist/organization-management/modules/organizations/pages/OrganizationDetailPage.js.map +1 -1
- package/dist/organization-management/modules/workspaces/components/WorkspaceForm.js +7 -7
- package/package.json +1 -1
|
@@ -1,44 +1,55 @@
|
|
|
1
1
|
import { OrgConsoleProvider as e } from "./providers/OrgConsoleProvider.js";
|
|
2
2
|
import { UserOrganizationRoutes as t } from "./UserOrganizationRoutes.js";
|
|
3
3
|
import { useEffect as n } from "react";
|
|
4
|
-
import { Building2 as r, Mail as i } from "lucide-react";
|
|
5
|
-
import { Toaster as
|
|
6
|
-
import { FeatureFlagProvider as
|
|
7
|
-
import { jsx as
|
|
4
|
+
import { Building2 as r, Mail as i, UserPlus as a } from "lucide-react";
|
|
5
|
+
import { Toaster as o } from "@burdenoff/fe-libs/ui";
|
|
6
|
+
import { FeatureFlagProvider as s } from "@burdenoff/fe-libs/shared/feature-flags";
|
|
7
|
+
import { jsx as c, jsxs as l } from "react/jsx-runtime";
|
|
8
8
|
//#region src/organization-management/UserOrganizationRoot.tsx
|
|
9
|
-
var
|
|
10
|
-
let { registerNavItems:
|
|
9
|
+
var u = (u) => {
|
|
10
|
+
let { registerNavItems: d, registerFooterItems: f, basePath: p = "/organizations" } = u, m = (e, t) => (e.endsWith("/") ? e.slice(0, -1) : e) + (t.startsWith("/") ? t : `/${t}`);
|
|
11
11
|
return n(() => {
|
|
12
|
-
if (
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
12
|
+
if (d) return d([
|
|
13
|
+
{
|
|
14
|
+
id: "user-org-organizations",
|
|
15
|
+
label: "Organizations",
|
|
16
|
+
path: p,
|
|
17
|
+
icon: /* @__PURE__ */ c(r, { size: 20 }),
|
|
18
|
+
order: 20,
|
|
19
|
+
group: "user"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: "user-org-invite-people",
|
|
23
|
+
label: "Invite People",
|
|
24
|
+
path: m(p, "/invite"),
|
|
25
|
+
icon: /* @__PURE__ */ c(a, { size: 20 }),
|
|
26
|
+
order: 25,
|
|
27
|
+
group: "user"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: "user-org-invites",
|
|
31
|
+
label: "My Invites",
|
|
32
|
+
path: m(p, "/invites"),
|
|
33
|
+
icon: /* @__PURE__ */ c(i, { size: 20 }),
|
|
34
|
+
order: 30,
|
|
35
|
+
group: "user"
|
|
36
|
+
}
|
|
37
|
+
]);
|
|
38
|
+
}, [d, p]), n(() => {
|
|
39
|
+
if (f) return f([{
|
|
29
40
|
id: "user-organization-version",
|
|
30
41
|
label: "Organization (User) v0.1.0",
|
|
31
42
|
type: "status",
|
|
32
43
|
order: 30,
|
|
33
44
|
section: "left"
|
|
34
45
|
}]);
|
|
35
|
-
}, [
|
|
46
|
+
}, [f]), /* @__PURE__ */ c(s, {
|
|
36
47
|
workspaceId: null,
|
|
37
48
|
gateway: "global",
|
|
38
49
|
defaultEnabled: !0,
|
|
39
|
-
children: /* @__PURE__ */
|
|
40
|
-
...
|
|
41
|
-
children: [/* @__PURE__ */
|
|
50
|
+
children: /* @__PURE__ */ l(e, {
|
|
51
|
+
...u,
|
|
52
|
+
children: [/* @__PURE__ */ c(t, {}), /* @__PURE__ */ c(o, {
|
|
42
53
|
position: "top-right",
|
|
43
54
|
richColors: !0,
|
|
44
55
|
closeButton: !0
|
|
@@ -47,6 +58,6 @@ var l = (l) => {
|
|
|
47
58
|
});
|
|
48
59
|
};
|
|
49
60
|
//#endregion
|
|
50
|
-
export {
|
|
61
|
+
export { u as UserOrganizationRoot };
|
|
51
62
|
|
|
52
63
|
//# 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 } 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';\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 { registerNavItems, registerFooterItems, basePath = '/organizations' } = props;\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: 'Organizations',\n path: basePath,\n icon: <Building2 size={20} />,\n order: 20,\n group: 'user',\n },\n {\n id: 'user-org-invites',\n label: '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]);\n\n useEffect(() => {\n if (!registerFooterItems) return;\n\n const footerItems = [\n {\n id: 'user-organization-version',\n label: '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]);\n\n return (\n <FeatureFlagProvider workspaceId={null} gateway=\"global\" defaultEnabled={true}>\n <OrgConsoleProvider {...props}>\n <UserOrganizationRoutes />\n <Toaster position=\"top-right\" richColors closeButton />\n </OrgConsoleProvider>\n </FeatureFlagProvider>\n );\n};\n"],"mappings":";;;;;;;;AA4BA,IAAa,KAAmD,MAAU;CACxE,IAAM,EAAE,qBAAkB,wBAAqB,cAAW,qBAAqB,GAEzE,KAAY,GAAc,OACZ,EAAK,SAAS,IAAI,GAAG,EAAK,MAAM,GAAG,GAAG,GAAG,MACzC,EAAK,WAAW,IAAI,GAAG,IAAO,IAAI;
|
|
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';\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 { registerNavItems, registerFooterItems, basePath = '/organizations' } = props;\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: '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: '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: '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]);\n\n useEffect(() => {\n if (!registerFooterItems) return;\n\n const footerItems = [\n {\n id: 'user-organization-version',\n label: '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]);\n\n return (\n <FeatureFlagProvider workspaceId={null} gateway=\"global\" defaultEnabled={true}>\n <OrgConsoleProvider {...props}>\n <UserOrganizationRoutes />\n <Toaster position=\"top-right\" richColors closeButton />\n </OrgConsoleProvider>\n </FeatureFlagProvider>\n );\n};\n"],"mappings":";;;;;;;;AA4BA,IAAa,KAAmD,MAAU;CACxE,IAAM,EAAE,qBAAkB,wBAAqB,cAAW,qBAAqB,GAEzE,KAAY,GAAc,OACZ,EAAK,SAAS,IAAI,GAAG,EAAK,MAAM,GAAG,GAAG,GAAG,MACzC,EAAK,WAAW,IAAI,GAAG,IAAO,IAAI;AAuDtD,QAnDA,QAAgB;AACT,QA8BL,QADgB,EA3BY;GAC1B;IACE,IAAI;IACJ,OAAO;IACP,MAAM;IACN,MAAM,kBAAC,GAAD,EAAW,MAAM,IAAM,CAAA;IAC7B,OAAO;IACP,OAAO;IACR;GACD;IACE,IAAI;IACJ,OAAO;IACP,MAAM,EAAS,GAAU,UAAU;IACnC,MAAM,kBAAC,GAAD,EAAU,MAAM,IAAM,CAAA;IAC5B,OAAO;IACP,OAAO;IACR;GACD;IACE,IAAI;IACJ,OAAO;IACP,MAAM,EAAS,GAAU,WAAW;IACpC,MAAM,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA;IACxB,OAAO;IACP,OAAO;IACR;GACF,CAEyC;IAEzC,CAAC,GAAkB,EAAS,CAAC,EAEhC,QAAgB;AACT,QAaL,QADgB,EAVI,CAClB;GACE,IAAI;GACJ,OAAO;GACP,MAAM;GACN,OAAO;GACP,SAAS;GACV,CACF,CAE+C;IAE/C,CAAC,EAAoB,CAAC,EAGvB,kBAAC,GAAD;EAAqB,aAAa;EAAM,SAAQ;EAAS,gBAAgB;YACvE,kBAAC,GAAD;GAAoB,GAAI;aAAxB,CACE,kBAAC,GAAD,EAA0B,CAAA,EAC1B,kBAAC,GAAD;IAAS,UAAS;IAAY,YAAA;IAAW,aAAA;IAAc,CAAA,CACpC;;EACD,CAAA"}
|
|
@@ -4,23 +4,23 @@ 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 { Outlet as o, Route as s, Routes as c, useParams as l } from "react-router-dom";
|
|
6
6
|
//#region src/organization-management/UserOrganizationRoutes.tsx
|
|
7
|
-
var u = r(() => import("./modules/organizations/pages/OrganizationCreatePage.js").then((e) => ({ default: e.OrganizationCreatePage }))), d = r(() => import("./modules/organizations/pages/OrganizationDetailPage.js").then((e) => ({ default: e.OrganizationDetailPage }))), f = r(() => import("./modules/organizations/pages/OrganizationEditPage.js").then((e) => ({ default: e.OrganizationEditPage }))), p = r(() => import("./modules/workspaces/pages/WorkspacesListPage.js").then((e) => ({ default: e.WorkspacesListPage }))), m = r(() => import("./modules/workspaces/pages/WorkspaceCreatePage.js").then((e) => ({ default: e.WorkspaceCreatePage }))), h = r(() => import("./modules/workspaces/pages/WorkspaceDetailPage.js").then((e) => ({ default: e.WorkspaceDetailPage }))), g = r(() => import("./modules/workspaces/pages/WorkspaceEditPage.js").then((e) => ({ default: e.WorkspaceEditPage }))), _ = r(() => import("./modules/members/pages/MembersListPage.js").then((e) => ({ default: e.MembersListPage }))), v = r(() => import("./modules/members/pages/MemberDetailPage.js").then((e) => ({ default: e.MemberDetailPage }))), y = r(() => import("./modules/members/pages/InviteMemberPage.js").then((e) => ({ default: e.InviteMemberPage }))), b = r(() => import("./modules/members/pages/MyInvitesPage.js").then((e) => ({ default: e.MyInvitesPage })));
|
|
8
|
-
function
|
|
7
|
+
var u = r(() => import("./modules/organizations/pages/OrganizationCreatePage.js").then((e) => ({ default: e.OrganizationCreatePage }))), d = r(() => import("./modules/organizations/pages/OrganizationDetailPage.js").then((e) => ({ default: e.OrganizationDetailPage }))), f = r(() => import("./modules/organizations/pages/OrganizationEditPage.js").then((e) => ({ default: e.OrganizationEditPage }))), p = r(() => import("./modules/workspaces/pages/WorkspacesListPage.js").then((e) => ({ default: e.WorkspacesListPage }))), m = r(() => import("./modules/workspaces/pages/WorkspaceCreatePage.js").then((e) => ({ default: e.WorkspaceCreatePage }))), h = r(() => import("./modules/workspaces/pages/WorkspaceDetailPage.js").then((e) => ({ default: e.WorkspaceDetailPage }))), g = r(() => import("./modules/workspaces/pages/WorkspaceEditPage.js").then((e) => ({ default: e.WorkspaceEditPage }))), _ = r(() => import("./modules/members/pages/MembersListPage.js").then((e) => ({ default: e.MembersListPage }))), v = r(() => import("./modules/members/pages/MemberDetailPage.js").then((e) => ({ default: e.MemberDetailPage }))), y = r(() => import("./modules/members/pages/InviteMemberPage.js").then((e) => ({ default: e.InviteMemberPage }))), b = r(() => import("./modules/members/pages/MyInvitesPage.js").then((e) => ({ default: e.MyInvitesPage }))), x = r(() => import("./modules/members/pages/InvitePeoplePage.js").then((e) => ({ default: e.InvitePeoplePage })));
|
|
8
|
+
function S({ paramName: e }) {
|
|
9
9
|
let n = l()[e];
|
|
10
10
|
return n ? /* @__PURE__ */ i(t, {
|
|
11
11
|
organizationId: n,
|
|
12
12
|
children: /* @__PURE__ */ i(o, {})
|
|
13
13
|
}) : /* @__PURE__ */ i(o, {});
|
|
14
14
|
}
|
|
15
|
-
function
|
|
15
|
+
function C() {
|
|
16
16
|
return /* @__PURE__ */ i("div", {
|
|
17
17
|
className: "flex items-center justify-center min-h-[200px]",
|
|
18
18
|
children: /* @__PURE__ */ i("div", { className: "size-8 animate-spin rounded-full border-2 border-[var(--color-border-default)] border-t-[var(--color-action-primary-bg)]" })
|
|
19
19
|
});
|
|
20
20
|
}
|
|
21
|
-
function
|
|
21
|
+
function w() {
|
|
22
22
|
return /* @__PURE__ */ i(n, {
|
|
23
|
-
fallback: /* @__PURE__ */ i(
|
|
23
|
+
fallback: /* @__PURE__ */ i(C, {}),
|
|
24
24
|
children: /* @__PURE__ */ a(c, { children: [
|
|
25
25
|
/* @__PURE__ */ i(s, {
|
|
26
26
|
index: !0,
|
|
@@ -32,7 +32,7 @@ function C() {
|
|
|
32
32
|
}),
|
|
33
33
|
/* @__PURE__ */ a(s, {
|
|
34
34
|
path: ":id",
|
|
35
|
-
element: /* @__PURE__ */ i(
|
|
35
|
+
element: /* @__PURE__ */ i(S, { paramName: "id" }),
|
|
36
36
|
children: [/* @__PURE__ */ i(s, {
|
|
37
37
|
index: !0,
|
|
38
38
|
element: /* @__PURE__ */ i(d, {})
|
|
@@ -45,6 +45,10 @@ function C() {
|
|
|
45
45
|
path: "invites",
|
|
46
46
|
element: /* @__PURE__ */ i(b, {})
|
|
47
47
|
}),
|
|
48
|
+
/* @__PURE__ */ i(s, {
|
|
49
|
+
path: "invite",
|
|
50
|
+
element: /* @__PURE__ */ i(x, {})
|
|
51
|
+
}),
|
|
48
52
|
/* @__PURE__ */ i(s, {
|
|
49
53
|
path: "workspaces",
|
|
50
54
|
element: /* @__PURE__ */ i(p, {})
|
|
@@ -63,7 +67,7 @@ function C() {
|
|
|
63
67
|
}),
|
|
64
68
|
/* @__PURE__ */ a(s, {
|
|
65
69
|
path: ":organizationId",
|
|
66
|
-
element: /* @__PURE__ */ i(
|
|
70
|
+
element: /* @__PURE__ */ i(S, { paramName: "organizationId" }),
|
|
67
71
|
children: [
|
|
68
72
|
/* @__PURE__ */ i(s, {
|
|
69
73
|
path: "members/invite",
|
|
@@ -83,6 +87,6 @@ function C() {
|
|
|
83
87
|
});
|
|
84
88
|
}
|
|
85
89
|
//#endregion
|
|
86
|
-
export {
|
|
90
|
+
export { w as UserOrganizationRoutes };
|
|
87
91
|
|
|
88
92
|
//# 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 { Outlet, Route, Routes, useParams } from 'react-router-dom';\n\n// Eager load — main entry point page\nimport { OrganizationsListPage } from './modules/organizations/pages/OrganizationsListPage';\nimport { OrgScopedPermissions } from './providers/OrgScopedPermissions';\n\n// Lazy load — secondary pages. Same set as the admin surface but the list\n// page below renders in `mode=\"my\"` (membership-scoped), and per-action\n// permission gates inside each page (hasPermission(PERMISSIONS.ORG_*))\n// already hide buttons the user can't use.\nconst OrganizationCreatePage = lazy(() =>\n import('./modules/organizations/pages/OrganizationCreatePage').then((m) => ({\n default: m.OrganizationCreatePage,\n }))\n);\nconst OrganizationDetailPage = lazy(() =>\n import('./modules/organizations/pages/OrganizationDetailPage').then((m) => ({\n default: m.OrganizationDetailPage,\n }))\n);\nconst OrganizationEditPage = lazy(() =>\n import('./modules/organizations/pages/OrganizationEditPage').then((m) => ({\n default: m.OrganizationEditPage,\n }))\n);\nconst WorkspacesListPage = lazy(() =>\n import('./modules/workspaces/pages/WorkspacesListPage').then((m) => ({\n default: m.WorkspacesListPage,\n }))\n);\nconst WorkspaceCreatePage = lazy(() =>\n import('./modules/workspaces/pages/WorkspaceCreatePage').then((m) => ({\n default: m.WorkspaceCreatePage,\n }))\n);\nconst WorkspaceDetailPage = lazy(() =>\n import('./modules/workspaces/pages/WorkspaceDetailPage').then((m) => ({\n default: m.WorkspaceDetailPage,\n }))\n);\nconst WorkspaceEditPage = lazy(() =>\n import('./modules/workspaces/pages/WorkspaceEditPage').then((m) => ({\n default: m.WorkspaceEditPage,\n }))\n);\nconst MembersListPage = lazy(() =>\n import('./modules/members/pages/MembersListPage').then((m) => ({ default: m.MembersListPage }))\n);\nconst MemberDetailPage = lazy(() =>\n import('./modules/members/pages/MemberDetailPage').then((m) => ({ default: m.MemberDetailPage }))\n);\nconst InviteMemberPage = lazy(() =>\n import('./modules/members/pages/InviteMemberPage').then((m) => ({ default: m.InviteMemberPage }))\n);\nconst MyInvitesPage = lazy(() =>\n import('./modules/members/pages/MyInvitesPage').then((m) => ({ default: m.MyInvitesPage }))\n);\n\n/**\n * Layout wrapper that scopes the PermissionProvider to the current\n * organization's id (read from the URL). This layout owns the `:id` or\n * `:organizationId` URL segment directly (see route declarations below),\n * so `useParams()[paramName]` is guaranteed to be populated and the\n * `OrgScopedPermissions` provider always wraps its descendants. The\n * previous shape mounted this on a pathless parent and relied on child\n * routes to declare the param, which could silently fall through to\n * tenant-scoped permissions if a future refactor moved the layout above\n * a tree that doesn't own the segment.\n */\nfunction OrgScopedRouteLayout({ paramName }: { paramName: 'id' | 'organizationId' }) {\n const params = useParams<Record<string, string | undefined>>();\n const orgId = params[paramName];\n if (!orgId) return <Outlet />;\n return (\n <OrgScopedPermissions organizationId={orgId}>\n <Outlet />\n </OrgScopedPermissions>\n );\n}\n\n/**\n * Loading fallback for lazy-loaded pages\n */\nfunction PageLoadingFallback() {\n return (\n <div className=\"flex items-center justify-center min-h-[200px]\">\n <div className=\"size-8 animate-spin rounded-full border-2 border-[var(--color-border-default)] border-t-[var(--color-action-primary-bg)]\" />\n </div>\n );\n}\n\n/**\n * UserOrganizationRoutes\n *\n * User-facing routing surface for the organization MFE, mounted by product\n * application shells (vibecontrols, etc.). Exposes the same shape as the\n * admin routing surface but the list page renders membership-scoped via\n * `mode=\"my\"` (calls `myOrganizations` with `@rbac(scopeType: \"self\")`).\n *\n * Per-action authorisation lives inside each page (`hasPermission` checks\n * for create / update / delete), so users with elevated permissions on an\n * org still see those controls; users without them only see view actions.\n *\n * All routes are unconditionally mounted. Feature flags\n * (`organization.create`, `organization.workspaces`, `organization.members`,\n * `organization.invite`) are applied at the call site — buttons, nav\n * items, section headers inside the pages — not on the route graph. This\n * avoids a class of route/nav mismatches where a page unconditionally\n * navigates to a URL that has been gated off (the previous flag-gated\n * routes produced dead URLs from `OrganizationDetailPage`'s workspace\n * cards, \"Create Workspace\" CTA, \"Manage Members\" button, etc.).\n *\n * Access control:\n * - Per-action `hasPermission` checks inside each page hide controls the\n * user can't use.\n * - Backend resolvers (`organization`, `organizationMembers`, etc.)\n * enforce membership server-side via `userHasAccessToOrganization`, so\n * deep-linking to a URL the user has no access to 403s at the resolver.\n *\n * Lazy loading strategy:\n * - OrganizationsListPage: Eager loaded (main entry point)\n * - All other pages: Lazy loaded for better initial bundle size\n */\nexport function UserOrganizationRoutes() {\n return (\n <Suspense fallback={<PageLoadingFallback />}>\n <Routes>\n {/* Organizations — index is the membership-scoped list (eager loaded) */}\n <Route index element={<OrganizationsListPage mode=\"my\" />} />\n <Route path=\"new\" element={<OrganizationCreatePage />} />\n\n {/* Per-org routes — the layout OWNS the `:id` segment so\n `useParams().id` is guaranteed to be set inside\n OrgScopedPermissions and per-page hasPermission() resolves at\n the org's scope, not the outer tenant scope. */}\n <Route path=\":id\" element={<OrgScopedRouteLayout paramName=\"id\" />}>\n <Route index element={<OrganizationDetailPage />} />\n <Route path=\"edit\" element={<OrganizationEditPage />} />\n </Route>\n\n {/* My Invites — user-level, not org-scoped. Declared before the\n `:organizationId` block so the literal \"invites\" wins the rank\n match against the dynamic segment. */}\n <Route path=\"invites\" element={<MyInvitesPage />} />\n\n {/* Workspaces — `OrganizationDetailPage` navigates here on workspace\n card click and the \"Create Workspace\" CTA. Declared with literal\n \"workspaces\" prefix so it ranks above the bare `:organizationId`\n dynamic-segment routes below. */}\n <Route path=\"workspaces\" element={<WorkspacesListPage />} />\n <Route path=\"workspaces/new\" element={<WorkspaceCreatePage />} />\n <Route path=\"workspaces/:id\" element={<WorkspaceDetailPage />} />\n <Route path=\"workspaces/:id/edit\" element={<WorkspaceEditPage />} />\n\n {/* Members — layout OWNS the `:organizationId` segment for the\n same reason as the detail block above. The inner pages already\n read `useParams().organizationId`, so we keep the segment name\n distinct from `:id` even though both resolve to an org id. */}\n <Route path=\":organizationId\" element={<OrgScopedRouteLayout paramName=\"organizationId\" />}>\n <Route path=\"members/invite\" element={<InviteMemberPage />} />\n <Route path=\"members\" element={<MembersListPage />} />\n <Route path=\"members/:userId\" element={<MemberDetailPage />} />\n </Route>\n </Routes>\n </Suspense>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"UserOrganizationRoutes.js","names":[],"sources":["../../src/organization-management/UserOrganizationRoutes.tsx"],"sourcesContent":["import { lazy, Suspense } from 'react';\nimport { Outlet, Route, Routes, useParams } from 'react-router-dom';\n\n// Eager load — main entry point page\nimport { OrganizationsListPage } from './modules/organizations/pages/OrganizationsListPage';\nimport { OrgScopedPermissions } from './providers/OrgScopedPermissions';\n\n// Lazy load — secondary pages. Same set as the admin surface but the list\n// page below renders in `mode=\"my\"` (membership-scoped), and per-action\n// permission gates inside each page (hasPermission(PERMISSIONS.ORG_*))\n// already hide buttons the user can't use.\nconst OrganizationCreatePage = lazy(() =>\n import('./modules/organizations/pages/OrganizationCreatePage').then((m) => ({\n default: m.OrganizationCreatePage,\n }))\n);\nconst OrganizationDetailPage = lazy(() =>\n import('./modules/organizations/pages/OrganizationDetailPage').then((m) => ({\n default: m.OrganizationDetailPage,\n }))\n);\nconst OrganizationEditPage = lazy(() =>\n import('./modules/organizations/pages/OrganizationEditPage').then((m) => ({\n default: m.OrganizationEditPage,\n }))\n);\nconst WorkspacesListPage = lazy(() =>\n import('./modules/workspaces/pages/WorkspacesListPage').then((m) => ({\n default: m.WorkspacesListPage,\n }))\n);\nconst WorkspaceCreatePage = lazy(() =>\n import('./modules/workspaces/pages/WorkspaceCreatePage').then((m) => ({\n default: m.WorkspaceCreatePage,\n }))\n);\nconst WorkspaceDetailPage = lazy(() =>\n import('./modules/workspaces/pages/WorkspaceDetailPage').then((m) => ({\n default: m.WorkspaceDetailPage,\n }))\n);\nconst WorkspaceEditPage = lazy(() =>\n import('./modules/workspaces/pages/WorkspaceEditPage').then((m) => ({\n default: m.WorkspaceEditPage,\n }))\n);\nconst MembersListPage = lazy(() =>\n import('./modules/members/pages/MembersListPage').then((m) => ({ default: m.MembersListPage }))\n);\nconst MemberDetailPage = lazy(() =>\n import('./modules/members/pages/MemberDetailPage').then((m) => ({ default: m.MemberDetailPage }))\n);\nconst InviteMemberPage = lazy(() =>\n import('./modules/members/pages/InviteMemberPage').then((m) => ({ default: m.InviteMemberPage }))\n);\nconst MyInvitesPage = lazy(() =>\n import('./modules/members/pages/MyInvitesPage').then((m) => ({ default: m.MyInvitesPage }))\n);\nconst InvitePeoplePage = lazy(() =>\n import('./modules/members/pages/InvitePeoplePage').then((m) => ({\n default: m.InvitePeoplePage,\n }))\n);\n\n/**\n * Layout wrapper that scopes the PermissionProvider to the current\n * organization's id (read from the URL). This layout owns the `:id` or\n * `:organizationId` URL segment directly (see route declarations below),\n * so `useParams()[paramName]` is guaranteed to be populated and the\n * `OrgScopedPermissions` provider always wraps its descendants. The\n * previous shape mounted this on a pathless parent and relied on child\n * routes to declare the param, which could silently fall through to\n * tenant-scoped permissions if a future refactor moved the layout above\n * a tree that doesn't own the segment.\n */\nfunction OrgScopedRouteLayout({ paramName }: { paramName: 'id' | 'organizationId' }) {\n const params = useParams<Record<string, string | undefined>>();\n const orgId = params[paramName];\n if (!orgId) return <Outlet />;\n return (\n <OrgScopedPermissions organizationId={orgId}>\n <Outlet />\n </OrgScopedPermissions>\n );\n}\n\n/**\n * Loading fallback for lazy-loaded pages\n */\nfunction PageLoadingFallback() {\n return (\n <div className=\"flex items-center justify-center min-h-[200px]\">\n <div className=\"size-8 animate-spin rounded-full border-2 border-[var(--color-border-default)] border-t-[var(--color-action-primary-bg)]\" />\n </div>\n );\n}\n\n/**\n * UserOrganizationRoutes\n *\n * User-facing routing surface for the organization MFE, mounted by product\n * application shells (vibecontrols, etc.). Exposes the same shape as the\n * admin routing surface but the list page renders membership-scoped via\n * `mode=\"my\"` (calls `myOrganizations` with `@rbac(scopeType: \"self\")`).\n *\n * Per-action authorisation lives inside each page (`hasPermission` checks\n * for create / update / delete), so users with elevated permissions on an\n * org still see those controls; users without them only see view actions.\n *\n * All routes are unconditionally mounted. Feature flags\n * (`organization.create`, `organization.workspaces`, `organization.members`,\n * `organization.invite`) are applied at the call site — buttons, nav\n * items, section headers inside the pages — not on the route graph. This\n * avoids a class of route/nav mismatches where a page unconditionally\n * navigates to a URL that has been gated off (the previous flag-gated\n * routes produced dead URLs from `OrganizationDetailPage`'s workspace\n * cards, \"Create Workspace\" CTA, \"Manage Members\" button, etc.).\n *\n * Access control:\n * - Per-action `hasPermission` checks inside each page hide controls the\n * user can't use.\n * - Backend resolvers (`organization`, `organizationMembers`, etc.)\n * enforce membership server-side via `userHasAccessToOrganization`, so\n * deep-linking to a URL the user has no access to 403s at the resolver.\n *\n * Lazy loading strategy:\n * - OrganizationsListPage: Eager loaded (main entry point)\n * - All other pages: Lazy loaded for better initial bundle size\n */\nexport function UserOrganizationRoutes() {\n return (\n <Suspense fallback={<PageLoadingFallback />}>\n <Routes>\n {/* Organizations — index is the membership-scoped list (eager loaded) */}\n <Route index element={<OrganizationsListPage mode=\"my\" />} />\n <Route path=\"new\" element={<OrganizationCreatePage />} />\n\n {/* Per-org routes — the layout OWNS the `:id` segment so\n `useParams().id` is guaranteed to be set inside\n OrgScopedPermissions and per-page hasPermission() resolves at\n the org's scope, not the outer tenant scope. */}\n <Route path=\":id\" element={<OrgScopedRouteLayout paramName=\"id\" />}>\n <Route index element={<OrganizationDetailPage />} />\n <Route path=\"edit\" element={<OrganizationEditPage />} />\n </Route>\n\n {/* My Invites — user-level, not org-scoped. Declared before the\n `:organizationId` block so the literal \"invites\" wins the rank\n match against the dynamic segment. */}\n <Route path=\"invites\" element={<MyInvitesPage />} />\n\n {/* Invite People — globally discoverable chooser. Lists orgs the\n user can invite into, then deep-links to per-org invite page. */}\n <Route path=\"invite\" element={<InvitePeoplePage />} />\n\n {/* Workspaces — `OrganizationDetailPage` navigates here on workspace\n card click and the \"Create Workspace\" CTA. Declared with literal\n \"workspaces\" prefix so it ranks above the bare `:organizationId`\n dynamic-segment routes below. */}\n <Route path=\"workspaces\" element={<WorkspacesListPage />} />\n <Route path=\"workspaces/new\" element={<WorkspaceCreatePage />} />\n <Route path=\"workspaces/:id\" element={<WorkspaceDetailPage />} />\n <Route path=\"workspaces/:id/edit\" element={<WorkspaceEditPage />} />\n\n {/* Members — layout OWNS the `:organizationId` segment for the\n same reason as the detail block above. The inner pages already\n read `useParams().organizationId`, so we keep the segment name\n distinct from `:id` even though both resolve to an org id. */}\n <Route path=\":organizationId\" element={<OrgScopedRouteLayout paramName=\"organizationId\" />}>\n <Route path=\"members/invite\" element={<InviteMemberPage />} />\n <Route path=\"members\" element={<MembersListPage />} />\n <Route path=\"members/:userId\" element={<MemberDetailPage />} />\n </Route>\n </Routes>\n </Suspense>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { useOrgConsole as e } from "../../../providers/OrgConsoleProvider.js";
|
|
2
|
+
import { useMyOrganizations as t } from "../../organizations/hooks/useOrganizations.js";
|
|
3
|
+
import { useTr as n } from "../../../shared/hooks/useTr.js";
|
|
4
|
+
import { LoadingState as r } from "../../../shared/components/feedback/LoadingState.js";
|
|
5
|
+
import { ErrorState as i } from "../../../shared/components/feedback/ErrorState.js";
|
|
6
|
+
import { EmptyState as a } from "../../../shared/components/feedback/EmptyState.js";
|
|
7
|
+
import { useOrganizationPageView as o } from "../../../hooks/useOrganizationPageView.js";
|
|
8
|
+
import { useOrgsWithPermission as s } from "../../organizations/hooks/useOrgsWithPermission.js";
|
|
9
|
+
import { useEffect as c, useMemo as l, useState as u } from "react";
|
|
10
|
+
import { Building as d, ChevronRight as f, UserPlus as p } from "lucide-react";
|
|
11
|
+
import { jsx as m, jsxs as h } from "react/jsx-runtime";
|
|
12
|
+
//#region src/organization-management/modules/members/pages/InvitePeoplePage.tsx
|
|
13
|
+
function g() {
|
|
14
|
+
o("organizations.invite-people");
|
|
15
|
+
let g = n(), { navigate: _, basePath: v = "/organizations" } = e(), { organizations: y, loading: b, error: x } = t(100, 0), { allowedIds: S, loading: C, isError: w } = s(l(() => y.map((e) => e.id), [y]), "member", "create"), T = l(() => y.filter((e) => S.has(e.id)), [y, S]), [E, D] = u(!1);
|
|
16
|
+
return c(() => {
|
|
17
|
+
E || b || C || T.length === 1 && (D(!0), _(`${v}/${T[0].id}/members/invite`));
|
|
18
|
+
}, [
|
|
19
|
+
E,
|
|
20
|
+
b,
|
|
21
|
+
C,
|
|
22
|
+
T,
|
|
23
|
+
_,
|
|
24
|
+
v
|
|
25
|
+
]), b || C ? /* @__PURE__ */ m("div", {
|
|
26
|
+
className: "min-h-full bg-[var(--color-bg-canvas)] p-6",
|
|
27
|
+
children: /* @__PURE__ */ m(r, { message: g("common.loading", "Loading...") })
|
|
28
|
+
}) : x || w ? /* @__PURE__ */ m("div", {
|
|
29
|
+
className: "min-h-full bg-[var(--color-bg-canvas)] p-6",
|
|
30
|
+
children: /* @__PURE__ */ m(i, { message: x?.message || g("members.invitePeople.error", "Failed to load organizations for invite.") })
|
|
31
|
+
}) : /* @__PURE__ */ m("div", {
|
|
32
|
+
className: "min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6 animate-in fade-in duration-300",
|
|
33
|
+
children: /* @__PURE__ */ h("div", {
|
|
34
|
+
className: "mx-auto max-w-3xl space-y-6",
|
|
35
|
+
children: [/* @__PURE__ */ h("div", {
|
|
36
|
+
className: "flex items-center gap-3",
|
|
37
|
+
children: [/* @__PURE__ */ m(p, {
|
|
38
|
+
size: 24,
|
|
39
|
+
className: "text-[var(--color-text-primary)]",
|
|
40
|
+
"aria-hidden": "true"
|
|
41
|
+
}), /* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("h1", {
|
|
42
|
+
className: "text-2xl font-bold text-[var(--color-text-primary)]",
|
|
43
|
+
children: g("members.invitePeople.title", "Invite People")
|
|
44
|
+
}), /* @__PURE__ */ m("p", {
|
|
45
|
+
className: "mt-1 text-sm text-[var(--color-text-secondary)]",
|
|
46
|
+
children: g("members.invitePeople.subtitle", "Pick an organization to send an invite into. You can also invite from any organization or workspace detail page.")
|
|
47
|
+
})] })]
|
|
48
|
+
}), T.length === 0 ? /* @__PURE__ */ m("div", {
|
|
49
|
+
className: "rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-6",
|
|
50
|
+
children: /* @__PURE__ */ m(a, {
|
|
51
|
+
icon: /* @__PURE__ */ m(p, {
|
|
52
|
+
size: 40,
|
|
53
|
+
"aria-hidden": "true"
|
|
54
|
+
}),
|
|
55
|
+
title: g("members.invitePeople.emptyTitle", "No organizations where you can invite"),
|
|
56
|
+
description: g("members.invitePeople.emptyDescription", "You don't currently have permission to invite members into any organization. Ask an organization admin to grant you invite access.")
|
|
57
|
+
})
|
|
58
|
+
}) : /* @__PURE__ */ m("ul", {
|
|
59
|
+
className: "divide-y divide-[var(--color-border-default)] rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] shadow-[var(--shadow-elevation-1)]",
|
|
60
|
+
children: T.map((e) => /* @__PURE__ */ m("li", { children: /* @__PURE__ */ h("button", {
|
|
61
|
+
type: "button",
|
|
62
|
+
onClick: () => _(`${v}/${e.id}/members/invite`),
|
|
63
|
+
className: "flex w-full items-center justify-between gap-4 px-5 py-4 text-left transition-colors hover:bg-[var(--color-bg-subtle)]",
|
|
64
|
+
children: [/* @__PURE__ */ h("div", {
|
|
65
|
+
className: "flex min-w-0 items-center gap-3",
|
|
66
|
+
children: [/* @__PURE__ */ m("div", {
|
|
67
|
+
className: "flex size-10 items-center justify-center rounded-lg bg-[var(--color-status-info-bgSubtle)] text-[var(--color-status-info-text)]",
|
|
68
|
+
children: /* @__PURE__ */ m(d, {
|
|
69
|
+
size: 20,
|
|
70
|
+
"aria-hidden": "true"
|
|
71
|
+
})
|
|
72
|
+
}), /* @__PURE__ */ h("div", {
|
|
73
|
+
className: "min-w-0",
|
|
74
|
+
children: [/* @__PURE__ */ m("p", {
|
|
75
|
+
className: "truncate text-sm font-semibold text-[var(--color-text-primary)]",
|
|
76
|
+
children: e.name
|
|
77
|
+
}), /* @__PURE__ */ h("p", {
|
|
78
|
+
className: "truncate font-mono text-xs text-[var(--color-text-secondary)]",
|
|
79
|
+
children: ["/", e.slug]
|
|
80
|
+
})]
|
|
81
|
+
})]
|
|
82
|
+
}), /* @__PURE__ */ m(f, {
|
|
83
|
+
size: 18,
|
|
84
|
+
className: "shrink-0 text-[var(--color-text-secondary)]",
|
|
85
|
+
"aria-hidden": "true"
|
|
86
|
+
})]
|
|
87
|
+
}) }, e.id))
|
|
88
|
+
})]
|
|
89
|
+
})
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
//#endregion
|
|
93
|
+
export { g as InvitePeoplePage };
|
|
94
|
+
|
|
95
|
+
//# sourceMappingURL=InvitePeoplePage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InvitePeoplePage.js","names":[],"sources":["../../../../../src/organization-management/modules/members/pages/InvitePeoplePage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { Building, ChevronRight, UserPlus } from 'lucide-react';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport { useMyOrganizations } from '../../organizations/hooks/useOrganizations';\nimport { useOrgsWithPermission } from '../../organizations/hooks/useOrgsWithPermission';\nimport { LoadingState } from '../../../shared/components/feedback/LoadingState';\nimport { ErrorState } from '../../../shared/components/feedback/ErrorState';\nimport { EmptyState } from '../../../shared/components/feedback/EmptyState';\nimport { useTr } from '../../../shared/hooks/useTr';\nimport { useOrganizationPageView } from '../../../hooks/useOrganizationPageView';\n\n/**\n * InvitePeoplePage\n *\n * Globally-reachable entry point (sidebar nav item) for inviting users.\n * Lists the orgs the current user can invite into, then deep-links into\n * each org's existing `members/invite` page so the invite flow itself\n * remains a single source of truth.\n *\n * If the user has exactly one org with invite permission, redirect\n * straight to that org's invite page — saves a click for the common case.\n */\nexport function InvitePeoplePage() {\n useOrganizationPageView('organizations.invite-people');\n const tr = useTr();\n const { navigate, basePath = '/organizations' } = useOrgConsole();\n const { organizations, loading, error } = useMyOrganizations(100, 0);\n\n const orgIds = useMemo(() => organizations.map((o) => o.id), [organizations]);\n const {\n allowedIds,\n loading: permLoading,\n isError: permError,\n } = useOrgsWithPermission(orgIds, 'member', 'create');\n\n const invitable = useMemo(\n () => organizations.filter((o) => allowedIds.has(o.id)),\n [organizations, allowedIds]\n );\n\n const [autoRedirected, setAutoRedirected] = useState(false);\n useEffect(() => {\n if (autoRedirected) return;\n if (loading || permLoading) return;\n if (invitable.length === 1) {\n setAutoRedirected(true);\n navigate(`${basePath}/${invitable[0].id}/members/invite`);\n }\n }, [autoRedirected, loading, permLoading, invitable, navigate, basePath]);\n\n if (loading || permLoading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <LoadingState message={tr('common.loading', 'Loading...')} />\n </div>\n );\n }\n\n if (error || permError) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <ErrorState\n message={\n error?.message ||\n tr('members.invitePeople.error', 'Failed to load organizations for invite.')\n }\n />\n </div>\n );\n }\n\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6 animate-in fade-in duration-300\">\n <div className=\"mx-auto max-w-3xl space-y-6\">\n <div className=\"flex items-center gap-3\">\n <UserPlus size={24} className=\"text-[var(--color-text-primary)]\" aria-hidden=\"true\" />\n <div>\n <h1 className=\"text-2xl font-bold text-[var(--color-text-primary)]\">\n {tr('members.invitePeople.title', 'Invite People')}\n </h1>\n <p className=\"mt-1 text-sm text-[var(--color-text-secondary)]\">\n {tr(\n 'members.invitePeople.subtitle',\n 'Pick an organization to send an invite into. You can also invite from any organization or workspace detail page.'\n )}\n </p>\n </div>\n </div>\n\n {invitable.length === 0 ? (\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-6\">\n <EmptyState\n icon={<UserPlus size={40} aria-hidden=\"true\" />}\n title={tr(\n 'members.invitePeople.emptyTitle',\n 'No organizations where you can invite'\n )}\n description={tr(\n 'members.invitePeople.emptyDescription',\n \"You don't currently have permission to invite members into any organization. Ask an organization admin to grant you invite access.\"\n )}\n />\n </div>\n ) : (\n <ul className=\"divide-y divide-[var(--color-border-default)] rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] shadow-[var(--shadow-elevation-1)]\">\n {invitable.map((org) => (\n <li key={org.id}>\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/${org.id}/members/invite`)}\n className=\"flex w-full items-center justify-between gap-4 px-5 py-4 text-left transition-colors hover:bg-[var(--color-bg-subtle)]\"\n >\n <div className=\"flex min-w-0 items-center gap-3\">\n <div className=\"flex size-10 items-center justify-center rounded-lg bg-[var(--color-status-info-bgSubtle)] text-[var(--color-status-info-text)]\">\n <Building size={20} aria-hidden=\"true\" />\n </div>\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-semibold text-[var(--color-text-primary)]\">\n {org.name}\n </p>\n <p className=\"truncate font-mono text-xs text-[var(--color-text-secondary)]\">\n /{org.slug}\n </p>\n </div>\n </div>\n <ChevronRight\n size={18}\n className=\"shrink-0 text-[var(--color-text-secondary)]\"\n aria-hidden=\"true\"\n />\n </button>\n </li>\n ))}\n </ul>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;AAsBA,SAAgB,IAAmB;AACjC,GAAwB,8BAA8B;CACtD,IAAM,IAAK,GAAO,EACZ,EAAE,aAAU,cAAW,qBAAqB,GAAe,EAC3D,EAAE,kBAAe,YAAS,aAAU,EAAmB,KAAK,EAAE,EAG9D,EACJ,eACA,SAAS,GACT,SAAS,MACP,EALW,QAAc,EAAc,KAAK,MAAM,EAAE,GAAG,EAAE,CAAC,EAAc,CAAC,EAK3C,UAAU,SAAS,EAE/C,IAAY,QACV,EAAc,QAAQ,MAAM,EAAW,IAAI,EAAE,GAAG,CAAC,EACvD,CAAC,GAAe,EAAW,CAC5B,EAEK,CAAC,GAAgB,KAAqB,EAAS,GAAM;AA+B3D,QA9BA,QAAgB;AACV,OACA,KAAW,KACX,EAAU,WAAW,MACvB,EAAkB,GAAK,EACvB,EAAS,GAAG,EAAS,GAAG,EAAU,GAAG,GAAG,iBAAiB;IAE1D;EAAC;EAAgB;EAAS;EAAa;EAAW;EAAU;EAAS,CAAC,EAErE,KAAW,IAEX,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD,EAAc,SAAS,EAAG,kBAAkB,aAAa,EAAI,CAAA;EACzD,CAAA,GAIN,KAAS,IAET,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD,EACE,SACE,GAAO,WACP,EAAG,8BAA8B,2CAA2C,EAE9E,CAAA;EACE,CAAA,GAKR,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAU,MAAM;KAAI,WAAU;KAAmC,eAAY;KAAS,CAAA,EACtF,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;KAAI,WAAU;eACX,EAAG,8BAA8B,gBAAgB;KAC/C,CAAA,EACL,kBAAC,KAAD;KAAG,WAAU;eACV,EACC,iCACA,mHACD;KACC,CAAA,CACA,EAAA,CAAA,CACF;OAEL,EAAU,WAAW,IACpB,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KACE,MAAM,kBAAC,GAAD;MAAU,MAAM;MAAI,eAAY;MAAS,CAAA;KAC/C,OAAO,EACL,mCACA,wCACD;KACD,aAAa,EACX,yCACA,qIACD;KACD,CAAA;IACE,CAAA,GAEN,kBAAC,MAAD;IAAI,WAAU;cACX,EAAU,KAAK,MACd,kBAAC,MAAD,EAAA,UACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAS,GAAG,EAAS,GAAG,EAAI,GAAG,iBAAiB;KAC/D,WAAU;eAHZ,CAKE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD;QAAU,MAAM;QAAI,eAAY;QAAS,CAAA;OACrC,CAAA,EACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAI;QACH,CAAA,EACJ,kBAAC,KAAD;QAAG,WAAU;kBAAb,CAA6E,KACzE,EAAI,KACJ;UACA;SACF;SACN,kBAAC,GAAD;MACE,MAAM;MACN,WAAU;MACV,eAAY;MACZ,CAAA,CACK;QACN,EAzBI,EAAI,GAyBR,CACL;IACC,CAAA,CAEH;;EACF,CAAA"}
|
|
@@ -10,12 +10,12 @@ import { useOrganizationPageView as c } from "../../../hooks/useOrganizationPage
|
|
|
10
10
|
import { useWorkspacesByOrganization as l } from "../../workspaces/hooks/useWorkspaces.js";
|
|
11
11
|
import { WorkspaceCard as u } from "../../workspaces/components/WorkspaceCard.js";
|
|
12
12
|
import { useMemo as d, useState as f } from "react";
|
|
13
|
-
import { ArrowLeft as p, Briefcase as m, Building as h, Edit as g,
|
|
14
|
-
import { usePermissions as
|
|
15
|
-
import { jsx as
|
|
16
|
-
import { useParams as
|
|
13
|
+
import { ArrowLeft as p, Briefcase as m, Building as h, Edit as g, UserPlus as _, Users as v } from "lucide-react";
|
|
14
|
+
import { usePermissions as y } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
15
|
+
import { jsx as b, jsxs as x } from "react/jsx-runtime";
|
|
16
|
+
import { useParams as S } from "react-router-dom";
|
|
17
17
|
//#region src/organization-management/modules/organizations/pages/OrganizationDetailPage.tsx
|
|
18
|
-
function
|
|
18
|
+
function C(e) {
|
|
19
19
|
return new Date(e).toLocaleDateString("en-US", {
|
|
20
20
|
year: "numeric",
|
|
21
21
|
month: "long",
|
|
@@ -24,272 +24,284 @@ function S(e) {
|
|
|
24
24
|
minute: "2-digit"
|
|
25
25
|
});
|
|
26
26
|
}
|
|
27
|
-
function
|
|
28
|
-
let
|
|
29
|
-
c("organizations.detail", { organizationId:
|
|
30
|
-
let { navigate:
|
|
27
|
+
function w() {
|
|
28
|
+
let w = r(), { id: T } = S();
|
|
29
|
+
c("organizations.detail", { organizationId: T });
|
|
30
|
+
let { navigate: E, basePath: D = "/organizations" } = e(), { hasPermission: O, isLoading: k } = y(), { organization: A, loading: j, error: M } = t(T ?? ""), [N, P] = f("overview"), { workspaces: F, loading: I, error: L, refetch: R } = l(N === "workspaces" && T ? T ?? "" : "", 50, 0), z = d(() => [{
|
|
31
31
|
id: "overview",
|
|
32
|
-
label:
|
|
32
|
+
label: w("organizations.detail.tab.overview", "Overview")
|
|
33
33
|
}, {
|
|
34
34
|
id: "workspaces",
|
|
35
|
-
label: `${
|
|
35
|
+
label: `${w("organizations.detail.tab.workspaces", "Workspaces")} (${N === "workspaces" ? F.length : A?.workspaceCount ?? 0})`
|
|
36
36
|
}], [
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
N,
|
|
38
|
+
A?.workspaceCount,
|
|
39
|
+
w,
|
|
40
|
+
F.length
|
|
41
41
|
]);
|
|
42
|
-
return
|
|
42
|
+
return k ? /* @__PURE__ */ b("div", {
|
|
43
43
|
className: "min-h-full bg-[var(--color-bg-canvas)] p-6",
|
|
44
|
-
children: /* @__PURE__ */
|
|
45
|
-
}) :
|
|
44
|
+
children: /* @__PURE__ */ b(i, { message: w("common.loadingPermissions", "Loading permissions...") })
|
|
45
|
+
}) : O(s.ORG_READ) ? j ? /* @__PURE__ */ b("div", {
|
|
46
46
|
className: "min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6",
|
|
47
|
-
children: /* @__PURE__ */
|
|
48
|
-
}) :
|
|
47
|
+
children: /* @__PURE__ */ b(i, { message: w("organizations.detail.loading", "Loading organization details...") })
|
|
48
|
+
}) : M || !A ? /* @__PURE__ */ b("div", {
|
|
49
49
|
className: "min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6",
|
|
50
|
-
children: /* @__PURE__ */
|
|
51
|
-
message:
|
|
50
|
+
children: /* @__PURE__ */ b(a, {
|
|
51
|
+
message: M?.message || w("organizations.detail.notFound", "Organization not found"),
|
|
52
52
|
action: {
|
|
53
|
-
label:
|
|
54
|
-
onClick: () =>
|
|
53
|
+
label: w("organizations.create.back", "Back to Organizations"),
|
|
54
|
+
onClick: () => E(D)
|
|
55
55
|
}
|
|
56
56
|
})
|
|
57
|
-
}) : /* @__PURE__ */
|
|
57
|
+
}) : /* @__PURE__ */ b("div", {
|
|
58
58
|
className: "min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6 animate-in fade-in duration-300",
|
|
59
|
-
children: /* @__PURE__ */
|
|
59
|
+
children: /* @__PURE__ */ x("div", {
|
|
60
60
|
className: "mx-auto flex w-full max-w-7xl flex-col gap-6",
|
|
61
|
-
children: [/* @__PURE__ */
|
|
61
|
+
children: [/* @__PURE__ */ x("div", {
|
|
62
62
|
className: "space-y-4",
|
|
63
|
-
children: [/* @__PURE__ */
|
|
63
|
+
children: [/* @__PURE__ */ x("button", {
|
|
64
64
|
type: "button",
|
|
65
|
-
onClick: () =>
|
|
65
|
+
onClick: () => E(D),
|
|
66
66
|
className: "inline-flex min-h-10 items-center gap-2 text-[var(--color-text-secondary)] transition-all duration-200 hover:text-[var(--color-text-primary)] active:scale-[0.98]",
|
|
67
|
-
children: [/* @__PURE__ */
|
|
67
|
+
children: [/* @__PURE__ */ b(p, {
|
|
68
68
|
size: 20,
|
|
69
69
|
"aria-hidden": "true"
|
|
70
|
-
}), /* @__PURE__ */
|
|
70
|
+
}), /* @__PURE__ */ b("span", {
|
|
71
71
|
className: "text-sm font-medium",
|
|
72
|
-
children:
|
|
72
|
+
children: w("organizations.create.back", "Back to Organizations")
|
|
73
73
|
})]
|
|
74
|
-
}), /* @__PURE__ */
|
|
74
|
+
}), /* @__PURE__ */ x("div", {
|
|
75
75
|
className: "flex flex-col gap-4 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-5 shadow-[var(--shadow-elevation-1)] lg:flex-row lg:items-start lg:justify-between",
|
|
76
|
-
children: [/* @__PURE__ */
|
|
76
|
+
children: [/* @__PURE__ */ x("div", {
|
|
77
77
|
className: "flex min-w-0 items-start gap-4",
|
|
78
|
-
children: [/* @__PURE__ */
|
|
78
|
+
children: [/* @__PURE__ */ b("div", {
|
|
79
79
|
className: "flex size-16 items-center justify-center rounded-xl bg-[var(--color-status-info-bgSubtle)] text-[var(--color-status-info-text)]",
|
|
80
|
-
children: /* @__PURE__ */
|
|
80
|
+
children: /* @__PURE__ */ b(h, {
|
|
81
81
|
size: 28,
|
|
82
82
|
"aria-hidden": "true"
|
|
83
83
|
})
|
|
84
|
-
}), /* @__PURE__ */
|
|
84
|
+
}), /* @__PURE__ */ x("div", {
|
|
85
85
|
className: "min-w-0 space-y-2",
|
|
86
86
|
children: [
|
|
87
|
-
/* @__PURE__ */
|
|
87
|
+
/* @__PURE__ */ x("div", {
|
|
88
88
|
className: "flex flex-wrap items-start gap-3",
|
|
89
|
-
children: [/* @__PURE__ */
|
|
89
|
+
children: [/* @__PURE__ */ b("h1", {
|
|
90
90
|
className: "text-3xl font-bold text-[var(--color-text-primary)]",
|
|
91
|
-
children:
|
|
92
|
-
}), /* @__PURE__ */
|
|
91
|
+
children: A.name
|
|
92
|
+
}), /* @__PURE__ */ b(n, { status: A.status })]
|
|
93
93
|
}),
|
|
94
|
-
/* @__PURE__ */
|
|
94
|
+
/* @__PURE__ */ x("p", {
|
|
95
95
|
className: "break-all font-mono text-sm text-[var(--color-text-secondary)]",
|
|
96
|
-
children: ["/",
|
|
96
|
+
children: ["/", A.slug]
|
|
97
97
|
}),
|
|
98
|
-
|
|
98
|
+
A.description ? /* @__PURE__ */ b("p", {
|
|
99
99
|
className: "max-w-2xl text-sm text-[var(--color-text-secondary)]",
|
|
100
|
-
children:
|
|
100
|
+
children: A.description
|
|
101
101
|
}) : null
|
|
102
102
|
]
|
|
103
103
|
})]
|
|
104
|
-
}), /* @__PURE__ */
|
|
104
|
+
}), /* @__PURE__ */ x("div", {
|
|
105
105
|
className: "flex flex-col gap-3 sm:flex-row",
|
|
106
|
-
children: [
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
106
|
+
children: [
|
|
107
|
+
O(s.MEMBER_LIST) && /* @__PURE__ */ x("button", {
|
|
108
|
+
type: "button",
|
|
109
|
+
onClick: () => E(`${D}/${T}/members`),
|
|
110
|
+
className: "inline-flex min-h-11 items-center justify-center gap-2 rounded-lg border border-[var(--color-border-default)] px-4 py-2.5 text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)]",
|
|
111
|
+
children: [/* @__PURE__ */ b(v, {
|
|
112
|
+
size: 16,
|
|
113
|
+
"aria-hidden": "true"
|
|
114
|
+
}), /* @__PURE__ */ b("span", { children: O(s.MEMBER_INVITE) ? w("organizations.detail.manageMembers", "Manage Members") : w("organizations.detail.viewMembers", "View Members") })]
|
|
115
|
+
}),
|
|
116
|
+
O(s.MEMBER_INVITE) && /* @__PURE__ */ x("button", {
|
|
117
|
+
type: "button",
|
|
118
|
+
onClick: () => E(`${D}/${T}/members/invite`),
|
|
119
|
+
className: "inline-flex min-h-11 items-center justify-center gap-2 rounded-lg border border-[var(--color-border-default)] px-4 py-2.5 text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)]",
|
|
120
|
+
children: [/* @__PURE__ */ b(_, {
|
|
121
|
+
size: 16,
|
|
122
|
+
"aria-hidden": "true"
|
|
123
|
+
}), /* @__PURE__ */ b("span", { children: w("members.invite.button", "Invite Member") })]
|
|
124
|
+
}),
|
|
125
|
+
O(s.ORG_UPDATE) && /* @__PURE__ */ x("button", {
|
|
126
|
+
type: "button",
|
|
127
|
+
onClick: () => E(`${D}/${T}/edit`),
|
|
128
|
+
className: "inline-flex min-h-11 items-center justify-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-4 py-2.5 text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90 active:scale-[0.98]",
|
|
129
|
+
children: [/* @__PURE__ */ b(g, {
|
|
130
|
+
size: 16,
|
|
131
|
+
"aria-hidden": "true"
|
|
132
|
+
}), /* @__PURE__ */ b("span", { children: w("organizations.edit.title", "Edit Organization") })]
|
|
133
|
+
})
|
|
134
|
+
]
|
|
123
135
|
})]
|
|
124
136
|
})]
|
|
125
|
-
}), /* @__PURE__ */
|
|
137
|
+
}), /* @__PURE__ */ x("div", {
|
|
126
138
|
className: "rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] shadow-[var(--shadow-elevation-1)]",
|
|
127
|
-
children: [/* @__PURE__ */
|
|
139
|
+
children: [/* @__PURE__ */ b("div", {
|
|
128
140
|
className: "overflow-x-auto border-b border-[var(--color-border-default)]",
|
|
129
|
-
children: /* @__PURE__ */
|
|
141
|
+
children: /* @__PURE__ */ b("div", {
|
|
130
142
|
className: "flex min-w-max gap-2 px-4 pt-4 sm:px-6",
|
|
131
|
-
children:
|
|
132
|
-
let t =
|
|
133
|
-
return /* @__PURE__ */
|
|
143
|
+
children: z.map((e) => {
|
|
144
|
+
let t = N === e.id;
|
|
145
|
+
return /* @__PURE__ */ b("button", {
|
|
134
146
|
type: "button",
|
|
135
|
-
onClick: () =>
|
|
147
|
+
onClick: () => P(e.id),
|
|
136
148
|
className: `inline-flex min-h-11 items-center rounded-t-lg border-b-2 px-4 py-3 text-sm font-medium transition-colors ${t ? "border-[var(--color-action-primary-bg)] text-[var(--color-action-primary-bg)]" : "border-transparent text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)]"}`,
|
|
137
149
|
"aria-pressed": t,
|
|
138
150
|
children: e.label
|
|
139
151
|
}, e.id);
|
|
140
152
|
})
|
|
141
153
|
})
|
|
142
|
-
}), /* @__PURE__ */
|
|
154
|
+
}), /* @__PURE__ */ b("div", {
|
|
143
155
|
className: "p-4 sm:p-6",
|
|
144
|
-
children:
|
|
156
|
+
children: N === "overview" ? /* @__PURE__ */ x("div", {
|
|
145
157
|
className: "grid grid-cols-1 gap-6 xl:grid-cols-[minmax(0,1fr)_320px]",
|
|
146
|
-
children: [/* @__PURE__ */
|
|
158
|
+
children: [/* @__PURE__ */ x("section", {
|
|
147
159
|
className: "rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-canvas)] p-5",
|
|
148
|
-
children: [/* @__PURE__ */
|
|
160
|
+
children: [/* @__PURE__ */ b("h2", {
|
|
149
161
|
className: "text-lg font-semibold text-[var(--color-text-primary)]",
|
|
150
|
-
children:
|
|
151
|
-
}), /* @__PURE__ */
|
|
162
|
+
children: w("organizations.detail.detailsSection", "Organization details")
|
|
163
|
+
}), /* @__PURE__ */ x("dl", {
|
|
152
164
|
className: "mt-5 grid grid-cols-1 gap-4 sm:grid-cols-2",
|
|
153
165
|
children: [
|
|
154
|
-
/* @__PURE__ */
|
|
166
|
+
/* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("dt", {
|
|
155
167
|
className: "text-sm font-medium text-[var(--color-text-secondary)]",
|
|
156
|
-
children:
|
|
157
|
-
}), /* @__PURE__ */
|
|
168
|
+
children: w("common.name", "Name")
|
|
169
|
+
}), /* @__PURE__ */ b("dd", {
|
|
158
170
|
className: "mt-1 text-sm text-[var(--color-text-primary)]",
|
|
159
|
-
children:
|
|
171
|
+
children: A.name
|
|
160
172
|
})] }),
|
|
161
|
-
/* @__PURE__ */
|
|
173
|
+
/* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("dt", {
|
|
162
174
|
className: "text-sm font-medium text-[var(--color-text-secondary)]",
|
|
163
|
-
children:
|
|
164
|
-
}), /* @__PURE__ */
|
|
175
|
+
children: w("common.slug", "Slug")
|
|
176
|
+
}), /* @__PURE__ */ b("dd", {
|
|
165
177
|
className: "mt-1 break-all font-mono text-sm text-[var(--color-text-primary)]",
|
|
166
|
-
children:
|
|
178
|
+
children: A.slug
|
|
167
179
|
})] }),
|
|
168
|
-
/* @__PURE__ */
|
|
180
|
+
/* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("dt", {
|
|
169
181
|
className: "text-sm font-medium text-[var(--color-text-secondary)]",
|
|
170
|
-
children:
|
|
171
|
-
}), /* @__PURE__ */
|
|
182
|
+
children: w("common.status", "Status")
|
|
183
|
+
}), /* @__PURE__ */ b("dd", {
|
|
172
184
|
className: "mt-1",
|
|
173
|
-
children: /* @__PURE__ */
|
|
185
|
+
children: /* @__PURE__ */ b(n, { status: A.status })
|
|
174
186
|
})] }),
|
|
175
|
-
/* @__PURE__ */
|
|
187
|
+
/* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("dt", {
|
|
176
188
|
className: "text-sm font-medium text-[var(--color-text-secondary)]",
|
|
177
|
-
children:
|
|
178
|
-
}), /* @__PURE__ */
|
|
189
|
+
children: w("organizations.detail.workspaceCount", "Workspace count")
|
|
190
|
+
}), /* @__PURE__ */ b("dd", {
|
|
179
191
|
className: "mt-1 text-sm text-[var(--color-text-primary)]",
|
|
180
|
-
children:
|
|
192
|
+
children: A.workspaceCount ?? 0
|
|
181
193
|
})] }),
|
|
182
|
-
|
|
194
|
+
A.website ? /* @__PURE__ */ x("div", {
|
|
183
195
|
className: "sm:col-span-2",
|
|
184
|
-
children: [/* @__PURE__ */
|
|
196
|
+
children: [/* @__PURE__ */ b("dt", {
|
|
185
197
|
className: "text-sm font-medium text-[var(--color-text-secondary)]",
|
|
186
|
-
children:
|
|
187
|
-
}), /* @__PURE__ */
|
|
198
|
+
children: w("organizations.detail.website", "Website")
|
|
199
|
+
}), /* @__PURE__ */ b("dd", {
|
|
188
200
|
className: "mt-1 break-all text-sm",
|
|
189
|
-
children: /* @__PURE__ */
|
|
190
|
-
href:
|
|
201
|
+
children: /* @__PURE__ */ b("a", {
|
|
202
|
+
href: A.website,
|
|
191
203
|
target: "_blank",
|
|
192
204
|
rel: "noreferrer",
|
|
193
205
|
className: "text-[var(--color-action-primary-bg)] underline-offset-2 hover:underline",
|
|
194
|
-
children:
|
|
206
|
+
children: A.website
|
|
195
207
|
})
|
|
196
208
|
})]
|
|
197
209
|
}) : null,
|
|
198
|
-
|
|
210
|
+
A.description ? /* @__PURE__ */ x("div", {
|
|
199
211
|
className: "sm:col-span-2",
|
|
200
|
-
children: [/* @__PURE__ */
|
|
212
|
+
children: [/* @__PURE__ */ b("dt", {
|
|
201
213
|
className: "text-sm font-medium text-[var(--color-text-secondary)]",
|
|
202
|
-
children:
|
|
203
|
-
}), /* @__PURE__ */
|
|
214
|
+
children: w("common.description", "Description")
|
|
215
|
+
}), /* @__PURE__ */ b("dd", {
|
|
204
216
|
className: "mt-1 text-sm leading-6 text-[var(--color-text-primary)]",
|
|
205
|
-
children:
|
|
217
|
+
children: A.description
|
|
206
218
|
})]
|
|
207
219
|
}) : null
|
|
208
220
|
]
|
|
209
221
|
})]
|
|
210
|
-
}), /* @__PURE__ */
|
|
222
|
+
}), /* @__PURE__ */ x("aside", {
|
|
211
223
|
className: "rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-canvas)] p-5",
|
|
212
|
-
children: [/* @__PURE__ */
|
|
224
|
+
children: [/* @__PURE__ */ b("h2", {
|
|
213
225
|
className: "text-lg font-semibold text-[var(--color-text-primary)]",
|
|
214
|
-
children:
|
|
215
|
-
}), /* @__PURE__ */
|
|
226
|
+
children: w("common.metadata", "Metadata")
|
|
227
|
+
}), /* @__PURE__ */ x("dl", {
|
|
216
228
|
className: "mt-5 space-y-4",
|
|
217
229
|
children: [
|
|
218
|
-
/* @__PURE__ */
|
|
230
|
+
/* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("dt", {
|
|
219
231
|
className: "text-sm font-medium text-[var(--color-text-secondary)]",
|
|
220
|
-
children:
|
|
221
|
-
}), /* @__PURE__ */
|
|
232
|
+
children: w("common.created", "Created")
|
|
233
|
+
}), /* @__PURE__ */ b("dd", {
|
|
222
234
|
className: "mt-1 text-sm text-[var(--color-text-primary)]",
|
|
223
|
-
children:
|
|
235
|
+
children: C(A.createdAt)
|
|
224
236
|
})] }),
|
|
225
|
-
/* @__PURE__ */
|
|
237
|
+
/* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("dt", {
|
|
226
238
|
className: "text-sm font-medium text-[var(--color-text-secondary)]",
|
|
227
|
-
children:
|
|
228
|
-
}), /* @__PURE__ */
|
|
239
|
+
children: w("common.lastUpdated", "Last updated")
|
|
240
|
+
}), /* @__PURE__ */ b("dd", {
|
|
229
241
|
className: "mt-1 text-sm text-[var(--color-text-primary)]",
|
|
230
|
-
children:
|
|
242
|
+
children: C(A.updatedAt)
|
|
231
243
|
})] }),
|
|
232
|
-
/* @__PURE__ */
|
|
244
|
+
/* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("dt", {
|
|
233
245
|
className: "text-sm font-medium text-[var(--color-text-secondary)]",
|
|
234
|
-
children:
|
|
235
|
-
}), /* @__PURE__ */
|
|
246
|
+
children: w("organizations.detail.tenantId", "Tenant ID")
|
|
247
|
+
}), /* @__PURE__ */ b("dd", {
|
|
236
248
|
className: "mt-1 break-all font-mono text-sm text-[var(--color-text-primary)]",
|
|
237
|
-
children:
|
|
249
|
+
children: A.tenantId
|
|
238
250
|
})] })
|
|
239
251
|
]
|
|
240
252
|
})]
|
|
241
253
|
})]
|
|
242
|
-
}) : /* @__PURE__ */
|
|
254
|
+
}) : /* @__PURE__ */ x("section", {
|
|
243
255
|
className: "space-y-4",
|
|
244
|
-
children: [/* @__PURE__ */
|
|
256
|
+
children: [/* @__PURE__ */ x("div", {
|
|
245
257
|
className: "flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",
|
|
246
|
-
children: [/* @__PURE__ */
|
|
258
|
+
children: [/* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("h2", {
|
|
247
259
|
className: "text-lg font-semibold text-[var(--color-text-primary)]",
|
|
248
|
-
children:
|
|
249
|
-
}), /* @__PURE__ */
|
|
260
|
+
children: w("organizations.detail.workspacesTitle", "Organization workspaces")
|
|
261
|
+
}), /* @__PURE__ */ b("p", {
|
|
250
262
|
className: "text-sm text-[var(--color-text-secondary)]",
|
|
251
|
-
children:
|
|
252
|
-
})] }),
|
|
263
|
+
children: w("organizations.detail.workspacesSubtitle", "Create and manage workspaces for this organization.")
|
|
264
|
+
})] }), O(s.WORKSPACE_CREATE) && /* @__PURE__ */ x("button", {
|
|
253
265
|
type: "button",
|
|
254
|
-
onClick: () =>
|
|
266
|
+
onClick: () => E(`${D}/workspaces/new?organizationId=${T}`),
|
|
255
267
|
className: "inline-flex min-h-11 items-center justify-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-4 py-2.5 text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90 active:scale-[0.98]",
|
|
256
|
-
children: [/* @__PURE__ */
|
|
268
|
+
children: [/* @__PURE__ */ b(m, {
|
|
257
269
|
size: 16,
|
|
258
270
|
"aria-hidden": "true"
|
|
259
|
-
}), /* @__PURE__ */
|
|
271
|
+
}), /* @__PURE__ */ b("span", { children: w("workspaces.create.button", "Create Workspace") })]
|
|
260
272
|
})]
|
|
261
|
-
}),
|
|
273
|
+
}), I ? /* @__PURE__ */ b("div", {
|
|
262
274
|
className: "py-8",
|
|
263
|
-
children: /* @__PURE__ */
|
|
264
|
-
}) :
|
|
265
|
-
message:
|
|
275
|
+
children: /* @__PURE__ */ b(i, { message: w("workspaces.list.loading", "Loading workspaces...") })
|
|
276
|
+
}) : L ? /* @__PURE__ */ b(a, {
|
|
277
|
+
message: L.message,
|
|
266
278
|
action: {
|
|
267
|
-
label:
|
|
279
|
+
label: w("common.retry", "Retry"),
|
|
268
280
|
onClick: () => {
|
|
269
|
-
|
|
281
|
+
R();
|
|
270
282
|
}
|
|
271
283
|
}
|
|
272
|
-
}) :
|
|
284
|
+
}) : F.length > 0 ? /* @__PURE__ */ b("div", {
|
|
273
285
|
className: "grid grid-cols-1 gap-4 xl:grid-cols-2",
|
|
274
|
-
children:
|
|
286
|
+
children: F.map((e) => /* @__PURE__ */ b(u, {
|
|
275
287
|
workspace: e,
|
|
276
288
|
showActions: !1,
|
|
277
289
|
onClick: (e) => {
|
|
278
|
-
|
|
290
|
+
E(`${D}/workspaces/${e.id}`);
|
|
279
291
|
}
|
|
280
292
|
}, e.id))
|
|
281
|
-
}) : /* @__PURE__ */
|
|
293
|
+
}) : /* @__PURE__ */ b("div", {
|
|
282
294
|
className: "rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-canvas)] p-6",
|
|
283
|
-
children: /* @__PURE__ */
|
|
284
|
-
icon: /* @__PURE__ */
|
|
295
|
+
children: /* @__PURE__ */ b(o, {
|
|
296
|
+
icon: /* @__PURE__ */ b(m, {
|
|
285
297
|
size: 40,
|
|
286
298
|
"aria-hidden": "true"
|
|
287
299
|
}),
|
|
288
|
-
title:
|
|
289
|
-
description:
|
|
290
|
-
action:
|
|
291
|
-
label:
|
|
292
|
-
onClick: () =>
|
|
300
|
+
title: w("workspaces.list.emptyTitle", "No workspaces yet"),
|
|
301
|
+
description: O(s.WORKSPACE_CREATE) ? w("workspaces.list.emptyDescription", "Create a workspace when this organization is ready to collaborate with teams or projects.") : w("workspaces.list.emptyDescriptionViewer", "No workspaces have been created in this organization yet."),
|
|
302
|
+
action: O(s.WORKSPACE_CREATE) ? {
|
|
303
|
+
label: w("workspaces.create.button", "Create Workspace"),
|
|
304
|
+
onClick: () => E(`${D}/workspaces/new?organizationId=${T}`)
|
|
293
305
|
} : void 0
|
|
294
306
|
})
|
|
295
307
|
})]
|
|
@@ -297,15 +309,15 @@ function C() {
|
|
|
297
309
|
})]
|
|
298
310
|
})]
|
|
299
311
|
})
|
|
300
|
-
}) : /* @__PURE__ */
|
|
312
|
+
}) : /* @__PURE__ */ b("div", {
|
|
301
313
|
className: "min-h-full bg-[var(--color-bg-canvas)] p-6",
|
|
302
|
-
children: /* @__PURE__ */
|
|
314
|
+
children: /* @__PURE__ */ b("div", {
|
|
303
315
|
className: "p-8 text-center text-[var(--color-text-secondary)]",
|
|
304
|
-
children:
|
|
316
|
+
children: w("organizations.detail.noPermission", "You don't have permission to view this organization.")
|
|
305
317
|
})
|
|
306
318
|
});
|
|
307
319
|
}
|
|
308
320
|
//#endregion
|
|
309
|
-
export {
|
|
321
|
+
export { w as OrganizationDetailPage };
|
|
310
322
|
|
|
311
323
|
//# sourceMappingURL=OrganizationDetailPage.js.map
|
package/dist/organization-management/modules/organizations/pages/OrganizationDetailPage.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OrganizationDetailPage.js","names":[],"sources":["../../../../../src/organization-management/modules/organizations/pages/OrganizationDetailPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { ArrowLeft, Briefcase, Building, Edit, Users } from 'lucide-react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport { useOrganization } from '../hooks/useOrganizations';\nimport { OrganizationStatusBadge } from '../components/OrganizationStatusBadge';\nimport { LoadingState } from '../../../shared/components/feedback/LoadingState';\nimport { ErrorState } from '../../../shared/components/feedback/ErrorState';\nimport { EmptyState } from '../../../shared/components/feedback/EmptyState';\nimport { useWorkspacesByOrganization } from '../../workspaces/hooks/useWorkspaces';\nimport { WorkspaceCard } from '../../workspaces/components/WorkspaceCard';\nimport { PERMISSIONS } from '../../../constants/permissions';\nimport { useTr } from '../../../shared/hooks/useTr';\nimport { useOrganizationPageView } from '../../../hooks/useOrganizationPageView';\n\ntype ActiveTab = 'overview' | 'workspaces';\n\nfunction formatDate(dateString: string) {\n return new Date(dateString).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n}\n\nexport function OrganizationDetailPage() {\n const tr = useTr();\n const { id } = useParams<{ id: string }>();\n useOrganizationPageView('organizations.detail', { organizationId: id });\n const { navigate, basePath = '/organizations' } = useOrgConsole();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const { organization, loading, error } = useOrganization(id ?? '');\n const [activeTab, setActiveTab] = useState<ActiveTab>('overview');\n\n const shouldFetchWorkspaces = activeTab === 'workspaces' && Boolean(id);\n const {\n workspaces,\n loading: workspacesLoading,\n error: workspacesError,\n refetch: refetchWorkspaces,\n } = useWorkspacesByOrganization(shouldFetchWorkspaces ? (id ?? '') : '', 50, 0);\n\n const tabItems = useMemo(\n () => [\n { id: 'overview' as const, label: tr('organizations.detail.tab.overview', 'Overview') },\n {\n id: 'workspaces' as const,\n label: `${tr('organizations.detail.tab.workspaces', 'Workspaces')} (${activeTab === 'workspaces' ? workspaces.length : (organization?.workspaceCount ?? 0)})`,\n },\n ],\n [activeTab, organization?.workspaceCount, tr, workspaces.length]\n );\n\n if (permissionsLoading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <LoadingState message={tr('common.loadingPermissions', 'Loading permissions...')} />\n </div>\n );\n }\n\n if (!hasPermission(PERMISSIONS.ORG_READ)) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <div className=\"p-8 text-center text-[var(--color-text-secondary)]\">\n {tr(\n 'organizations.detail.noPermission',\n \"You don't have permission to view this organization.\"\n )}\n </div>\n </div>\n );\n }\n\n if (loading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <LoadingState\n message={tr('organizations.detail.loading', 'Loading organization details...')}\n />\n </div>\n );\n }\n\n if (error || !organization) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <ErrorState\n message={error?.message || tr('organizations.detail.notFound', 'Organization not found')}\n action={{\n label: tr('organizations.create.back', 'Back to Organizations'),\n onClick: () => navigate(basePath),\n }}\n />\n </div>\n );\n }\n\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6 animate-in fade-in duration-300\">\n <div className=\"mx-auto flex w-full max-w-7xl flex-col gap-6\">\n <div className=\"space-y-4\">\n <button\n type=\"button\"\n onClick={() => navigate(basePath)}\n className=\"inline-flex min-h-10 items-center gap-2 text-[var(--color-text-secondary)] transition-all duration-200 hover:text-[var(--color-text-primary)] active:scale-[0.98]\"\n >\n <ArrowLeft size={20} aria-hidden=\"true\" />\n <span className=\"text-sm font-medium\">\n {tr('organizations.create.back', 'Back to Organizations')}\n </span>\n </button>\n\n <div className=\"flex flex-col gap-4 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-5 shadow-[var(--shadow-elevation-1)] lg:flex-row lg:items-start lg:justify-between\">\n <div className=\"flex min-w-0 items-start gap-4\">\n <div className=\"flex size-16 items-center justify-center rounded-xl bg-[var(--color-status-info-bgSubtle)] text-[var(--color-status-info-text)]\">\n <Building size={28} aria-hidden=\"true\" />\n </div>\n\n <div className=\"min-w-0 space-y-2\">\n <div className=\"flex flex-wrap items-start gap-3\">\n <h1 className=\"text-3xl font-bold text-[var(--color-text-primary)]\">\n {organization.name}\n </h1>\n <OrganizationStatusBadge status={organization.status} />\n </div>\n <p className=\"break-all font-mono text-sm text-[var(--color-text-secondary)]\">\n /{organization.slug}\n </p>\n {organization.description ? (\n <p className=\"max-w-2xl text-sm text-[var(--color-text-secondary)]\">\n {organization.description}\n </p>\n ) : null}\n </div>\n </div>\n\n <div className=\"flex flex-col gap-3 sm:flex-row\">\n {hasPermission(PERMISSIONS.MEMBER_LIST) && (\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/${id}/members`)}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg border border-[var(--color-border-default)] px-4 py-2.5 text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)]\"\n >\n <Users size={16} aria-hidden=\"true\" />\n <span>\n {hasPermission(PERMISSIONS.MEMBER_INVITE)\n ? tr('organizations.detail.manageMembers', 'Manage Members')\n : tr('organizations.detail.viewMembers', 'View Members')}\n </span>\n </button>\n )}\n {hasPermission(PERMISSIONS.ORG_UPDATE) && (\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/${id}/edit`)}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-4 py-2.5 text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90 active:scale-[0.98]\"\n >\n <Edit size={16} aria-hidden=\"true\" />\n <span>{tr('organizations.edit.title', 'Edit Organization')}</span>\n </button>\n )}\n </div>\n </div>\n </div>\n\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] shadow-[var(--shadow-elevation-1)]\">\n <div className=\"overflow-x-auto border-b border-[var(--color-border-default)]\">\n <div className=\"flex min-w-max gap-2 px-4 pt-4 sm:px-6\">\n {tabItems.map((tabItem) => {\n const isActive = activeTab === tabItem.id;\n return (\n <button\n type=\"button\"\n key={tabItem.id}\n onClick={() => setActiveTab(tabItem.id)}\n className={`inline-flex min-h-11 items-center rounded-t-lg border-b-2 px-4 py-3 text-sm font-medium transition-colors ${\n isActive\n ? 'border-[var(--color-action-primary-bg)] text-[var(--color-action-primary-bg)]'\n : 'border-transparent text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)]'\n }`}\n aria-pressed={isActive}\n >\n {tabItem.label}\n </button>\n );\n })}\n </div>\n </div>\n\n <div className=\"p-4 sm:p-6\">\n {activeTab === 'overview' ? (\n <div className=\"grid grid-cols-1 gap-6 xl:grid-cols-[minmax(0,1fr)_320px]\">\n <section className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-canvas)] p-5\">\n <h2 className=\"text-lg font-semibold text-[var(--color-text-primary)]\">\n {tr('organizations.detail.detailsSection', 'Organization details')}\n </h2>\n\n <dl className=\"mt-5 grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('common.name', 'Name')}\n </dt>\n <dd className=\"mt-1 text-sm text-[var(--color-text-primary)]\">\n {organization.name}\n </dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('common.slug', 'Slug')}\n </dt>\n <dd className=\"mt-1 break-all font-mono text-sm text-[var(--color-text-primary)]\">\n {organization.slug}\n </dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('common.status', 'Status')}\n </dt>\n <dd className=\"mt-1\">\n <OrganizationStatusBadge status={organization.status} />\n </dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('organizations.detail.workspaceCount', 'Workspace count')}\n </dt>\n <dd className=\"mt-1 text-sm text-[var(--color-text-primary)]\">\n {organization.workspaceCount ?? 0}\n </dd>\n </div>\n {organization.website ? (\n <div className=\"sm:col-span-2\">\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('organizations.detail.website', 'Website')}\n </dt>\n <dd className=\"mt-1 break-all text-sm\">\n <a\n href={organization.website}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"text-[var(--color-action-primary-bg)] underline-offset-2 hover:underline\"\n >\n {organization.website}\n </a>\n </dd>\n </div>\n ) : null}\n {organization.description ? (\n <div className=\"sm:col-span-2\">\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('common.description', 'Description')}\n </dt>\n <dd className=\"mt-1 text-sm leading-6 text-[var(--color-text-primary)]\">\n {organization.description}\n </dd>\n </div>\n ) : null}\n </dl>\n </section>\n\n <aside className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-canvas)] p-5\">\n <h2 className=\"text-lg font-semibold text-[var(--color-text-primary)]\">\n {tr('common.metadata', 'Metadata')}\n </h2>\n\n <dl className=\"mt-5 space-y-4\">\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('common.created', 'Created')}\n </dt>\n <dd className=\"mt-1 text-sm text-[var(--color-text-primary)]\">\n {formatDate(organization.createdAt)}\n </dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('common.lastUpdated', 'Last updated')}\n </dt>\n <dd className=\"mt-1 text-sm text-[var(--color-text-primary)]\">\n {formatDate(organization.updatedAt)}\n </dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('organizations.detail.tenantId', 'Tenant ID')}\n </dt>\n <dd className=\"mt-1 break-all font-mono text-sm text-[var(--color-text-primary)]\">\n {organization.tenantId}\n </dd>\n </div>\n </dl>\n </aside>\n </div>\n ) : (\n <section className=\"space-y-4\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h2 className=\"text-lg font-semibold text-[var(--color-text-primary)]\">\n {tr('organizations.detail.workspacesTitle', 'Organization workspaces')}\n </h2>\n <p className=\"text-sm text-[var(--color-text-secondary)]\">\n {tr(\n 'organizations.detail.workspacesSubtitle',\n 'Create and manage workspaces for this organization.'\n )}\n </p>\n </div>\n\n {hasPermission(PERMISSIONS.WORKSPACE_CREATE) && (\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/workspaces/new?organizationId=${id}`)}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-4 py-2.5 text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90 active:scale-[0.98]\"\n >\n <Briefcase size={16} aria-hidden=\"true\" />\n <span>{tr('workspaces.create.button', 'Create Workspace')}</span>\n </button>\n )}\n </div>\n\n {workspacesLoading ? (\n <div className=\"py-8\">\n <LoadingState\n message={tr('workspaces.list.loading', 'Loading workspaces...')}\n />\n </div>\n ) : workspacesError ? (\n <ErrorState\n message={workspacesError.message}\n action={{\n label: tr('common.retry', 'Retry'),\n onClick: () => {\n void refetchWorkspaces();\n },\n }}\n />\n ) : workspaces.length > 0 ? (\n <div className=\"grid grid-cols-1 gap-4 xl:grid-cols-2\">\n {workspaces.map((workspace) => (\n <WorkspaceCard\n key={workspace.id}\n workspace={workspace}\n showActions={false}\n onClick={(selectedWorkspace) => {\n navigate(`${basePath}/workspaces/${selectedWorkspace.id}`);\n }}\n />\n ))}\n </div>\n ) : (\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-canvas)] p-6\">\n <EmptyState\n icon={<Briefcase size={40} aria-hidden=\"true\" />}\n title={tr('workspaces.list.emptyTitle', 'No workspaces yet')}\n description={\n hasPermission(PERMISSIONS.WORKSPACE_CREATE)\n ? tr(\n 'workspaces.list.emptyDescription',\n 'Create a workspace when this organization is ready to collaborate with teams or projects.'\n )\n : tr(\n 'workspaces.list.emptyDescriptionViewer',\n 'No workspaces have been created in this organization yet.'\n )\n }\n action={\n hasPermission(PERMISSIONS.WORKSPACE_CREATE)\n ? {\n label: tr('workspaces.create.button', 'Create Workspace'),\n onClick: () =>\n navigate(`${basePath}/workspaces/new?organizationId=${id}`),\n }\n : undefined\n }\n />\n </div>\n )}\n </section>\n )}\n </div>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"OrganizationDetailPage.js","names":[],"sources":["../../../../../src/organization-management/modules/organizations/pages/OrganizationDetailPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { ArrowLeft, Briefcase, Building, Edit, UserPlus, Users } from 'lucide-react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useOrgConsole } from '../../../providers/OrgConsoleProvider';\nimport { useOrganization } from '../hooks/useOrganizations';\nimport { OrganizationStatusBadge } from '../components/OrganizationStatusBadge';\nimport { LoadingState } from '../../../shared/components/feedback/LoadingState';\nimport { ErrorState } from '../../../shared/components/feedback/ErrorState';\nimport { EmptyState } from '../../../shared/components/feedback/EmptyState';\nimport { useWorkspacesByOrganization } from '../../workspaces/hooks/useWorkspaces';\nimport { WorkspaceCard } from '../../workspaces/components/WorkspaceCard';\nimport { PERMISSIONS } from '../../../constants/permissions';\nimport { useTr } from '../../../shared/hooks/useTr';\nimport { useOrganizationPageView } from '../../../hooks/useOrganizationPageView';\n\ntype ActiveTab = 'overview' | 'workspaces';\n\nfunction formatDate(dateString: string) {\n return new Date(dateString).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n}\n\nexport function OrganizationDetailPage() {\n const tr = useTr();\n const { id } = useParams<{ id: string }>();\n useOrganizationPageView('organizations.detail', { organizationId: id });\n const { navigate, basePath = '/organizations' } = useOrgConsole();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const { organization, loading, error } = useOrganization(id ?? '');\n const [activeTab, setActiveTab] = useState<ActiveTab>('overview');\n\n const shouldFetchWorkspaces = activeTab === 'workspaces' && Boolean(id);\n const {\n workspaces,\n loading: workspacesLoading,\n error: workspacesError,\n refetch: refetchWorkspaces,\n } = useWorkspacesByOrganization(shouldFetchWorkspaces ? (id ?? '') : '', 50, 0);\n\n const tabItems = useMemo(\n () => [\n { id: 'overview' as const, label: tr('organizations.detail.tab.overview', 'Overview') },\n {\n id: 'workspaces' as const,\n label: `${tr('organizations.detail.tab.workspaces', 'Workspaces')} (${activeTab === 'workspaces' ? workspaces.length : (organization?.workspaceCount ?? 0)})`,\n },\n ],\n [activeTab, organization?.workspaceCount, tr, workspaces.length]\n );\n\n if (permissionsLoading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <LoadingState message={tr('common.loadingPermissions', 'Loading permissions...')} />\n </div>\n );\n }\n\n if (!hasPermission(PERMISSIONS.ORG_READ)) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] p-6\">\n <div className=\"p-8 text-center text-[var(--color-text-secondary)]\">\n {tr(\n 'organizations.detail.noPermission',\n \"You don't have permission to view this organization.\"\n )}\n </div>\n </div>\n );\n }\n\n if (loading) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <LoadingState\n message={tr('organizations.detail.loading', 'Loading organization details...')}\n />\n </div>\n );\n }\n\n if (error || !organization) {\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6\">\n <ErrorState\n message={error?.message || tr('organizations.detail.notFound', 'Organization not found')}\n action={{\n label: tr('organizations.create.back', 'Back to Organizations'),\n onClick: () => navigate(basePath),\n }}\n />\n </div>\n );\n }\n\n return (\n <div className=\"min-h-full bg-[var(--color-bg-canvas)] px-4 py-6 sm:px-6 animate-in fade-in duration-300\">\n <div className=\"mx-auto flex w-full max-w-7xl flex-col gap-6\">\n <div className=\"space-y-4\">\n <button\n type=\"button\"\n onClick={() => navigate(basePath)}\n className=\"inline-flex min-h-10 items-center gap-2 text-[var(--color-text-secondary)] transition-all duration-200 hover:text-[var(--color-text-primary)] active:scale-[0.98]\"\n >\n <ArrowLeft size={20} aria-hidden=\"true\" />\n <span className=\"text-sm font-medium\">\n {tr('organizations.create.back', 'Back to Organizations')}\n </span>\n </button>\n\n <div className=\"flex flex-col gap-4 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] p-5 shadow-[var(--shadow-elevation-1)] lg:flex-row lg:items-start lg:justify-between\">\n <div className=\"flex min-w-0 items-start gap-4\">\n <div className=\"flex size-16 items-center justify-center rounded-xl bg-[var(--color-status-info-bgSubtle)] text-[var(--color-status-info-text)]\">\n <Building size={28} aria-hidden=\"true\" />\n </div>\n\n <div className=\"min-w-0 space-y-2\">\n <div className=\"flex flex-wrap items-start gap-3\">\n <h1 className=\"text-3xl font-bold text-[var(--color-text-primary)]\">\n {organization.name}\n </h1>\n <OrganizationStatusBadge status={organization.status} />\n </div>\n <p className=\"break-all font-mono text-sm text-[var(--color-text-secondary)]\">\n /{organization.slug}\n </p>\n {organization.description ? (\n <p className=\"max-w-2xl text-sm text-[var(--color-text-secondary)]\">\n {organization.description}\n </p>\n ) : null}\n </div>\n </div>\n\n <div className=\"flex flex-col gap-3 sm:flex-row\">\n {hasPermission(PERMISSIONS.MEMBER_LIST) && (\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/${id}/members`)}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg border border-[var(--color-border-default)] px-4 py-2.5 text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)]\"\n >\n <Users size={16} aria-hidden=\"true\" />\n <span>\n {hasPermission(PERMISSIONS.MEMBER_INVITE)\n ? tr('organizations.detail.manageMembers', 'Manage Members')\n : tr('organizations.detail.viewMembers', 'View Members')}\n </span>\n </button>\n )}\n {hasPermission(PERMISSIONS.MEMBER_INVITE) && (\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/${id}/members/invite`)}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg border border-[var(--color-border-default)] px-4 py-2.5 text-[var(--color-text-primary)] transition-all duration-200 hover:bg-[var(--color-bg-subtle)]\"\n >\n <UserPlus size={16} aria-hidden=\"true\" />\n <span>{tr('members.invite.button', 'Invite Member')}</span>\n </button>\n )}\n {hasPermission(PERMISSIONS.ORG_UPDATE) && (\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/${id}/edit`)}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-4 py-2.5 text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90 active:scale-[0.98]\"\n >\n <Edit size={16} aria-hidden=\"true\" />\n <span>{tr('organizations.edit.title', 'Edit Organization')}</span>\n </button>\n )}\n </div>\n </div>\n </div>\n\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] shadow-[var(--shadow-elevation-1)]\">\n <div className=\"overflow-x-auto border-b border-[var(--color-border-default)]\">\n <div className=\"flex min-w-max gap-2 px-4 pt-4 sm:px-6\">\n {tabItems.map((tabItem) => {\n const isActive = activeTab === tabItem.id;\n return (\n <button\n type=\"button\"\n key={tabItem.id}\n onClick={() => setActiveTab(tabItem.id)}\n className={`inline-flex min-h-11 items-center rounded-t-lg border-b-2 px-4 py-3 text-sm font-medium transition-colors ${\n isActive\n ? 'border-[var(--color-action-primary-bg)] text-[var(--color-action-primary-bg)]'\n : 'border-transparent text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)]'\n }`}\n aria-pressed={isActive}\n >\n {tabItem.label}\n </button>\n );\n })}\n </div>\n </div>\n\n <div className=\"p-4 sm:p-6\">\n {activeTab === 'overview' ? (\n <div className=\"grid grid-cols-1 gap-6 xl:grid-cols-[minmax(0,1fr)_320px]\">\n <section className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-canvas)] p-5\">\n <h2 className=\"text-lg font-semibold text-[var(--color-text-primary)]\">\n {tr('organizations.detail.detailsSection', 'Organization details')}\n </h2>\n\n <dl className=\"mt-5 grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('common.name', 'Name')}\n </dt>\n <dd className=\"mt-1 text-sm text-[var(--color-text-primary)]\">\n {organization.name}\n </dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('common.slug', 'Slug')}\n </dt>\n <dd className=\"mt-1 break-all font-mono text-sm text-[var(--color-text-primary)]\">\n {organization.slug}\n </dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('common.status', 'Status')}\n </dt>\n <dd className=\"mt-1\">\n <OrganizationStatusBadge status={organization.status} />\n </dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('organizations.detail.workspaceCount', 'Workspace count')}\n </dt>\n <dd className=\"mt-1 text-sm text-[var(--color-text-primary)]\">\n {organization.workspaceCount ?? 0}\n </dd>\n </div>\n {organization.website ? (\n <div className=\"sm:col-span-2\">\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('organizations.detail.website', 'Website')}\n </dt>\n <dd className=\"mt-1 break-all text-sm\">\n <a\n href={organization.website}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"text-[var(--color-action-primary-bg)] underline-offset-2 hover:underline\"\n >\n {organization.website}\n </a>\n </dd>\n </div>\n ) : null}\n {organization.description ? (\n <div className=\"sm:col-span-2\">\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('common.description', 'Description')}\n </dt>\n <dd className=\"mt-1 text-sm leading-6 text-[var(--color-text-primary)]\">\n {organization.description}\n </dd>\n </div>\n ) : null}\n </dl>\n </section>\n\n <aside className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-canvas)] p-5\">\n <h2 className=\"text-lg font-semibold text-[var(--color-text-primary)]\">\n {tr('common.metadata', 'Metadata')}\n </h2>\n\n <dl className=\"mt-5 space-y-4\">\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('common.created', 'Created')}\n </dt>\n <dd className=\"mt-1 text-sm text-[var(--color-text-primary)]\">\n {formatDate(organization.createdAt)}\n </dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('common.lastUpdated', 'Last updated')}\n </dt>\n <dd className=\"mt-1 text-sm text-[var(--color-text-primary)]\">\n {formatDate(organization.updatedAt)}\n </dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-[var(--color-text-secondary)]\">\n {tr('organizations.detail.tenantId', 'Tenant ID')}\n </dt>\n <dd className=\"mt-1 break-all font-mono text-sm text-[var(--color-text-primary)]\">\n {organization.tenantId}\n </dd>\n </div>\n </dl>\n </aside>\n </div>\n ) : (\n <section className=\"space-y-4\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h2 className=\"text-lg font-semibold text-[var(--color-text-primary)]\">\n {tr('organizations.detail.workspacesTitle', 'Organization workspaces')}\n </h2>\n <p className=\"text-sm text-[var(--color-text-secondary)]\">\n {tr(\n 'organizations.detail.workspacesSubtitle',\n 'Create and manage workspaces for this organization.'\n )}\n </p>\n </div>\n\n {hasPermission(PERMISSIONS.WORKSPACE_CREATE) && (\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/workspaces/new?organizationId=${id}`)}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-lg bg-[var(--color-action-primary-bg)] px-4 py-2.5 text-[var(--color-action-primary-text)] transition-all duration-200 hover:opacity-90 active:scale-[0.98]\"\n >\n <Briefcase size={16} aria-hidden=\"true\" />\n <span>{tr('workspaces.create.button', 'Create Workspace')}</span>\n </button>\n )}\n </div>\n\n {workspacesLoading ? (\n <div className=\"py-8\">\n <LoadingState\n message={tr('workspaces.list.loading', 'Loading workspaces...')}\n />\n </div>\n ) : workspacesError ? (\n <ErrorState\n message={workspacesError.message}\n action={{\n label: tr('common.retry', 'Retry'),\n onClick: () => {\n void refetchWorkspaces();\n },\n }}\n />\n ) : workspaces.length > 0 ? (\n <div className=\"grid grid-cols-1 gap-4 xl:grid-cols-2\">\n {workspaces.map((workspace) => (\n <WorkspaceCard\n key={workspace.id}\n workspace={workspace}\n showActions={false}\n onClick={(selectedWorkspace) => {\n navigate(`${basePath}/workspaces/${selectedWorkspace.id}`);\n }}\n />\n ))}\n </div>\n ) : (\n <div className=\"rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-canvas)] p-6\">\n <EmptyState\n icon={<Briefcase size={40} aria-hidden=\"true\" />}\n title={tr('workspaces.list.emptyTitle', 'No workspaces yet')}\n description={\n hasPermission(PERMISSIONS.WORKSPACE_CREATE)\n ? tr(\n 'workspaces.list.emptyDescription',\n 'Create a workspace when this organization is ready to collaborate with teams or projects.'\n )\n : tr(\n 'workspaces.list.emptyDescriptionViewer',\n 'No workspaces have been created in this organization yet.'\n )\n }\n action={\n hasPermission(PERMISSIONS.WORKSPACE_CREATE)\n ? {\n label: tr('workspaces.create.button', 'Create Workspace'),\n onClick: () =>\n navigate(`${basePath}/workspaces/new?organizationId=${id}`),\n }\n : undefined\n }\n />\n </div>\n )}\n </section>\n )}\n </div>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { useOrganizations as e } from "../../organizations/hooks/useOrganizations.js";
|
|
2
2
|
import { useTr as t } from "../../../shared/hooks/useTr.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import { useOrgsWithPermission as n } from "../../organizations/hooks/useOrgsWithPermission.js";
|
|
4
|
+
import { useTenants as r } from "../hooks/useWorkspaces.js";
|
|
5
|
+
import { ZodError as i } from "../../../../node_modules/zod/v3/ZodError.js";
|
|
6
|
+
import { createWorkspaceSchema as a, updateWorkspaceSchema as o } from "../schemas/index.js";
|
|
7
7
|
import { useEffect as s, useMemo as c, useState as l } from "react";
|
|
8
8
|
import { Building as u, Home as d, Loader2 as f } from "lucide-react";
|
|
9
9
|
import { Fragment as p, jsx as m, jsxs as h } from "react/jsx-runtime";
|
|
@@ -15,7 +15,7 @@ function g({ workspace: g, tenantId: _, tenantName: v, organizationId: y, organi
|
|
|
15
15
|
type: g?.type || "PERSONAL",
|
|
16
16
|
tenantId: _ || g?.tenantId || "",
|
|
17
17
|
organizationId: y || g?.organizationId || ""
|
|
18
|
-
}), [O, k] = l({}), A = !g && x && !_, j = !g && x && !y, { tenants: M, loading: N } =
|
|
18
|
+
}), [O, k] = l({}), A = !g && x && !_, j = !g && x && !y, { tenants: M, loading: N } = r(100, 0, A), { organizations: P, loading: F } = e(E.tenantId ? { tenantId: E.tenantId } : void 0, 100, 0, j), { allowedIds: I, loading: L } = n(c(() => P.map((e) => e.id), [P]), "workspace", "create"), R = !g && x && !y, z = c(() => R ? P.filter((e) => I.has(e.id)) : P, [
|
|
19
19
|
P,
|
|
20
20
|
I,
|
|
21
21
|
R
|
|
@@ -51,9 +51,9 @@ function g({ workspace: g, tenantId: _, tenantName: v, organizationId: y, organi
|
|
|
51
51
|
z
|
|
52
52
|
]), H = () => {
|
|
53
53
|
try {
|
|
54
|
-
return (g ?
|
|
54
|
+
return (g ? o : a).parse(E), k({}), !0;
|
|
55
55
|
} catch (e) {
|
|
56
|
-
if (e instanceof
|
|
56
|
+
if (e instanceof i) {
|
|
57
57
|
let t = {};
|
|
58
58
|
e.issues.forEach((e) => {
|
|
59
59
|
let n = e.path.join(".");
|