@burdenoff/microfe-workspaces 2026.531.2 → 2026.531.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/WorkspacesRoutes.js +36 -17
- package/dist/WorkspacesRoutes.js.map +1 -1
- package/dist/components/InviteMemberDialog.js +204 -117
- package/dist/components/InviteMemberDialog.js.map +1 -1
- package/dist/components/MemberListItem.js +2 -1
- package/dist/components/MemberListItem.js.map +1 -1
- package/dist/components/ProjectMemberPickerDialog.js +211 -0
- package/dist/components/ProjectMemberPickerDialog.js.map +1 -0
- package/dist/hooks/index.js +1 -1
- package/dist/hooks/useMemberMutations.js +13 -13
- package/dist/hooks/useMemberMutations.js.map +1 -1
- package/dist/hooks/useProjectMemberMutations.js +104 -0
- package/dist/hooks/useProjectMemberMutations.js.map +1 -0
- package/dist/hooks/useProjectMembers.js +42 -0
- package/dist/hooks/useProjectMembers.js.map +1 -0
- package/dist/hooks/useWorkspaceMembers.js +60 -24
- package/dist/hooks/useWorkspaceMembers.js.map +1 -1
- package/dist/hooks/useWorkspacePermissions.js +4 -0
- package/dist/hooks/useWorkspacePermissions.js.map +1 -1
- package/dist/hooks/useWorkspacesEventEmitter.js.map +1 -1
- package/dist/index.js +16 -15
- package/dist/pages/ActivityListPage.js +5 -5
- package/dist/pages/InviteAcceptancePage.js +211 -207
- package/dist/pages/InviteAcceptancePage.js.map +1 -1
- package/dist/pages/MembersListPage.js +210 -208
- package/dist/pages/MembersListPage.js.map +1 -1
- package/dist/pages/PendingInvitesPage.js +124 -120
- package/dist/pages/PendingInvitesPage.js.map +1 -1
- package/dist/pages/ProjectDetailPage.js +249 -213
- package/dist/pages/ProjectDetailPage.js.map +1 -1
- package/dist/pages/ProjectMembersPage.js +265 -0
- package/dist/pages/ProjectMembersPage.js.map +1 -0
- package/dist/pages/ProjectsListPage.js +9 -9
- package/dist/pages/WorkspaceDetailPage.js +119 -111
- package/dist/pages/WorkspaceDetailPage.js.map +1 -1
- package/dist/pages/WorkspaceInvitePage.js +790 -0
- package/dist/pages/WorkspaceInvitePage.js.map +1 -0
- package/dist/pages/WorkspacesListPage.js +115 -100
- package/dist/pages/WorkspacesListPage.js.map +1 -1
- package/dist/utils/invitationDisplay.js +25 -0
- package/dist/utils/invitationDisplay.js.map +1 -0
- package/dist/utils/workspaceBulkInviteCsv.js +42 -0
- package/dist/utils/workspaceBulkInviteCsv.js.map +1 -0
- package/package.json +1 -1
package/dist/WorkspacesRoutes.js
CHANGED
|
@@ -5,14 +5,14 @@ import { FeatureGate as i } from "@burdenoff/fe-libs/shared/feature-flags";
|
|
|
5
5
|
import { jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
6
6
|
import { Navigate as s, Route as c, Routes as l } from "react-router-dom";
|
|
7
7
|
//#region src/WorkspacesRoutes.tsx
|
|
8
|
-
var u = r(() => import("./pages/CreateWorkspacePage.js").then((e) => ({ default: e.CreateWorkspacePage }))), d = r(() => import("./pages/WorkspaceDetailPage.js").then((e) => ({ default: e.WorkspaceDetailPage }))), f = r(() => import("./pages/WorkspaceSettingsPage.js").then((e) => ({ default: e.WorkspaceSettingsPage }))), p = r(() => import("./pages/ProjectsListPage.js").then((e) => ({ default: e.ProjectsListPage }))), m = r(() => import("./pages/ProjectDetailPage.js").then((e) => ({ default: e.ProjectDetailPage }))), h = r(() => import("./pages/MembersListPage.js").then((e) => ({ default: e.MembersListPage }))),
|
|
9
|
-
function
|
|
8
|
+
var u = r(() => import("./pages/CreateWorkspacePage.js").then((e) => ({ default: e.CreateWorkspacePage }))), d = r(() => import("./pages/WorkspaceDetailPage.js").then((e) => ({ default: e.WorkspaceDetailPage }))), f = r(() => import("./pages/WorkspaceSettingsPage.js").then((e) => ({ default: e.WorkspaceSettingsPage }))), p = r(() => import("./pages/ProjectsListPage.js").then((e) => ({ default: e.ProjectsListPage }))), m = r(() => import("./pages/ProjectDetailPage.js").then((e) => ({ default: e.ProjectDetailPage }))), h = r(() => import("./pages/ProjectMembersPage.js").then((e) => ({ default: e.ProjectMembersPage }))), g = r(() => import("./pages/MembersListPage.js").then((e) => ({ default: e.MembersListPage }))), _ = r(() => import("./pages/ActivityListPage.js").then((e) => ({ default: e.ActivityListPage }))), v = r(() => import("./pages/InviteAcceptancePage.js").then((e) => ({ default: e.InviteAcceptancePage }))), y = r(() => import("./pages/PendingInvitesPage.js").then((e) => ({ default: e.PendingInvitesPage }))), b = r(() => import("./pages/WorkspaceInvitePage.js").then((e) => ({ default: e.WorkspaceInvitePage })));
|
|
9
|
+
function x() {
|
|
10
10
|
return /* @__PURE__ */ a("div", {
|
|
11
11
|
className: "flex items-center justify-center h-64",
|
|
12
12
|
children: /* @__PURE__ */ a("div", { className: "animate-spin rounded-full size-8 border-b-2 border-primary" })
|
|
13
13
|
});
|
|
14
14
|
}
|
|
15
|
-
function
|
|
15
|
+
function S() {
|
|
16
16
|
return /* @__PURE__ */ a("div", {
|
|
17
17
|
className: "flex h-full items-center justify-center p-8 text-center",
|
|
18
18
|
children: /* @__PURE__ */ o("div", { children: [/* @__PURE__ */ a("h2", {
|
|
@@ -24,24 +24,31 @@ function b() {
|
|
|
24
24
|
})] })
|
|
25
25
|
});
|
|
26
26
|
}
|
|
27
|
-
function
|
|
27
|
+
function C(e, t, n = !1) {
|
|
28
28
|
return /* @__PURE__ */ a(i, {
|
|
29
29
|
flag: e,
|
|
30
30
|
defaultValue: n,
|
|
31
|
-
fallback: /* @__PURE__ */ a(
|
|
31
|
+
fallback: /* @__PURE__ */ a(S, {}),
|
|
32
32
|
children: t
|
|
33
33
|
});
|
|
34
34
|
}
|
|
35
|
-
function
|
|
35
|
+
function w() {
|
|
36
36
|
let { basePath: t = "/workspaces" } = e();
|
|
37
37
|
return /* @__PURE__ */ a(s, {
|
|
38
38
|
to: `${t}/list`,
|
|
39
39
|
replace: !0
|
|
40
40
|
});
|
|
41
41
|
}
|
|
42
|
-
function
|
|
42
|
+
function T() {
|
|
43
|
+
let { basePath: t = "/workspaces", workspaceId: n } = e();
|
|
44
|
+
return n ? /* @__PURE__ */ a(s, {
|
|
45
|
+
to: `${t}/${n}/invite`,
|
|
46
|
+
replace: !0
|
|
47
|
+
}) : /* @__PURE__ */ a(b, {});
|
|
48
|
+
}
|
|
49
|
+
function E() {
|
|
43
50
|
return /* @__PURE__ */ a(n, {
|
|
44
|
-
fallback: /* @__PURE__ */ a(
|
|
51
|
+
fallback: /* @__PURE__ */ a(x, {}),
|
|
45
52
|
children: /* @__PURE__ */ o(l, { children: [
|
|
46
53
|
/* @__PURE__ */ a(c, {
|
|
47
54
|
index: !0,
|
|
@@ -60,11 +67,15 @@ function C() {
|
|
|
60
67
|
}),
|
|
61
68
|
/* @__PURE__ */ a(c, {
|
|
62
69
|
path: "invites",
|
|
63
|
-
element: /* @__PURE__ */ a(
|
|
70
|
+
element: /* @__PURE__ */ a(y, {})
|
|
71
|
+
}),
|
|
72
|
+
/* @__PURE__ */ a(c, {
|
|
73
|
+
path: "invite",
|
|
74
|
+
element: /* @__PURE__ */ a(T, {})
|
|
64
75
|
}),
|
|
65
76
|
/* @__PURE__ */ a(c, {
|
|
66
77
|
path: "invite/:invitationId",
|
|
67
|
-
element: /* @__PURE__ */ a(
|
|
78
|
+
element: /* @__PURE__ */ a(v, {})
|
|
68
79
|
}),
|
|
69
80
|
/* @__PURE__ */ a(c, {
|
|
70
81
|
path: ":workspaceId",
|
|
@@ -72,7 +83,11 @@ function C() {
|
|
|
72
83
|
}),
|
|
73
84
|
/* @__PURE__ */ a(c, {
|
|
74
85
|
path: ":workspaceId/settings",
|
|
75
|
-
element:
|
|
86
|
+
element: C("workspaces.settings", /* @__PURE__ */ a(f, {}), !0)
|
|
87
|
+
}),
|
|
88
|
+
/* @__PURE__ */ a(c, {
|
|
89
|
+
path: ":workspaceId/invite",
|
|
90
|
+
element: /* @__PURE__ */ a(b, {})
|
|
76
91
|
}),
|
|
77
92
|
/* @__PURE__ */ a(c, {
|
|
78
93
|
path: "projects",
|
|
@@ -86,30 +101,34 @@ function C() {
|
|
|
86
101
|
path: ":workspaceId/projects/:projectId",
|
|
87
102
|
element: /* @__PURE__ */ a(m, {})
|
|
88
103
|
}),
|
|
104
|
+
/* @__PURE__ */ a(c, {
|
|
105
|
+
path: ":workspaceId/projects/:projectId/members",
|
|
106
|
+
element: C("workspaces.members", /* @__PURE__ */ a(h, {}), !0)
|
|
107
|
+
}),
|
|
89
108
|
/* @__PURE__ */ a(c, {
|
|
90
109
|
path: "members",
|
|
91
|
-
element:
|
|
110
|
+
element: C("workspaces.members", /* @__PURE__ */ a(g, {}), !0)
|
|
92
111
|
}),
|
|
93
112
|
/* @__PURE__ */ a(c, {
|
|
94
113
|
path: ":workspaceId/members",
|
|
95
|
-
element:
|
|
114
|
+
element: C("workspaces.members", /* @__PURE__ */ a(g, {}), !0)
|
|
96
115
|
}),
|
|
97
116
|
/* @__PURE__ */ a(c, {
|
|
98
117
|
path: "activity",
|
|
99
|
-
element:
|
|
118
|
+
element: C("workspaces.activity", /* @__PURE__ */ a(_, {}))
|
|
100
119
|
}),
|
|
101
120
|
/* @__PURE__ */ a(c, {
|
|
102
121
|
path: ":workspaceId/activity",
|
|
103
|
-
element:
|
|
122
|
+
element: C("workspaces.activity", /* @__PURE__ */ a(_, {}))
|
|
104
123
|
}),
|
|
105
124
|
/* @__PURE__ */ a(c, {
|
|
106
125
|
path: "*",
|
|
107
|
-
element: /* @__PURE__ */ a(
|
|
126
|
+
element: /* @__PURE__ */ a(w, {})
|
|
108
127
|
})
|
|
109
128
|
] })
|
|
110
129
|
});
|
|
111
130
|
}
|
|
112
131
|
//#endregion
|
|
113
|
-
export {
|
|
132
|
+
export { E as WorkspacesRoutes };
|
|
114
133
|
|
|
115
134
|
//# sourceMappingURL=WorkspacesRoutes.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkspacesRoutes.js","names":[],"sources":["../src/WorkspacesRoutes.tsx"],"sourcesContent":["import { lazy, Suspense } from 'react';\nimport { Routes, Route, Navigate } from 'react-router-dom';\nimport { FeatureGate } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { useWorkspacesContext } from './providers/WorkspacesProvider';\n\n// Eager load - main entry point\nimport { WorkspacesListPage } from './pages/WorkspacesListPage';\n\n// Lazy load - secondary pages\nconst CreateWorkspacePage = lazy(() =>\n import('./pages/CreateWorkspacePage').then((m) => ({ default: m.CreateWorkspacePage }))\n);\nconst WorkspaceDetailPage = lazy(() =>\n import('./pages/WorkspaceDetailPage').then((m) => ({ default: m.WorkspaceDetailPage }))\n);\nconst WorkspaceSettingsPage = lazy(() =>\n import('./pages/WorkspaceSettingsPage').then((m) => ({ default: m.WorkspaceSettingsPage }))\n);\nconst ProjectsListPage = lazy(() =>\n import('./pages/ProjectsListPage').then((m) => ({ default: m.ProjectsListPage }))\n);\nconst ProjectDetailPage = lazy(() =>\n import('./pages/ProjectDetailPage').then((m) => ({ default: m.ProjectDetailPage }))\n);\nconst MembersListPage = lazy(() =>\n import('./pages/MembersListPage').then((m) => ({ default: m.MembersListPage }))\n);\nconst ActivityListPage = lazy(() =>\n import('./pages/ActivityListPage').then((m) => ({ default: m.ActivityListPage }))\n);\nconst InviteAcceptancePage = lazy(() =>\n import('./pages/InviteAcceptancePage').then((m) => ({ default: m.InviteAcceptancePage }))\n);\nconst PendingInvitesPage = lazy(() =>\n import('./pages/PendingInvitesPage').then((m) => ({ default: m.PendingInvitesPage }))\n);\n\n/**\n * Loading fallback for lazy-loaded components\n */\nfunction PageLoader() {\n return (\n <div className=\"flex items-center justify-center h-64\">\n <div className=\"animate-spin rounded-full size-8 border-b-2 border-primary\" />\n </div>\n );\n}\n\n/**\n * Fallback shown when a feature-gated route is disabled.\n */\nfunction FeatureDisabledFallback() {\n return (\n <div className=\"flex h-full items-center justify-center p-8 text-center\">\n <div>\n <h2 className=\"text-lg font-semibold mb-2 text-text-primary\">Feature Not Available</h2>\n <p className=\"text-sm text-text-secondary\">\n This feature is not currently available. Enable it under Feature Previews to access this\n functionality.\n </p>\n </div>\n </div>\n );\n}\n\n/**\n * Helper: wraps a page element in a FeatureGate.\n * defaultValue=false → hidden until explicitly enabled (unreleased feature).\n */\nfunction gated(flag: string, element: React.ReactNode, defaultValue = false): React.ReactElement {\n return (\n <FeatureGate flag={flag} defaultValue={defaultValue} fallback={<FeatureDisabledFallback />}>\n {element}\n </FeatureGate>\n );\n}\n\n/**\n * Catch-all redirect component that uses the basePath from context\n * to navigate to an absolute path, preventing infinite redirect loops.\n */\nfunction CatchAllRedirect() {\n const { basePath = '/workspaces' } = useWorkspacesContext();\n return <Navigate to={`${basePath}/list`} replace />;\n}\n\n/**\n * Internal routing for the Workspaces microfrontend.\n * Defines all routes and their corresponding page components.\n *\n * - WorkspacesListPage: Eager loaded (main entry point)\n * - All other pages: Lazy loaded for better performance\n * - Projects, Members, Activity: gated by feature flags (unreleased)\n */\nexport function WorkspacesRoutes() {\n return (\n <Suspense fallback={<PageLoader />}>\n <Routes>\n {/* Default redirect */}\n <Route index element={<Navigate to=\"list\" replace />} />\n\n {/* Workspaces List - Eager loaded */}\n <Route path=\"list\" element={<WorkspacesListPage />} />\n\n {/* Create Workspace (must come before :workspaceId to avoid conflicts) */}\n <Route path=\"new\" element={<CreateWorkspacePage />} />\n\n {/* Pending Invites (must come before :workspaceId to avoid conflicts) */}\n <Route path=\"invites\" element={<PendingInvitesPage />} />\n\n {/* Invitation Acceptance (must come before :workspaceId to avoid conflicts) */}\n <Route path=\"invite/:invitationId\" element={<InviteAcceptancePage />} />\n\n {/* Workspace Detail */}\n <Route path=\":workspaceId\" element={<WorkspaceDetailPage />} />\n <Route\n path=\":workspaceId/settings\"\n element={gated('workspaces.settings', <WorkspaceSettingsPage />, true)}\n />\n\n {/* Projects — core feature, never gated */}\n <Route path=\"projects\" element={<ProjectsListPage />} />\n <Route path=\":workspaceId/projects\" element={<ProjectsListPage />} />\n <Route path=\":workspaceId/projects/:projectId\" element={<ProjectDetailPage />} />\n\n {/* Members — gated by workspaces.members */}\n <Route path=\"members\" element={gated('workspaces.members', <MembersListPage />, true)} />\n <Route\n path=\":workspaceId/members\"\n element={gated('workspaces.members', <MembersListPage />, true)}\n />\n\n {/* Activity — gated by workspaces.activity */}\n <Route path=\"activity\" element={gated('workspaces.activity', <ActivityListPage />)} />\n <Route\n path=\":workspaceId/activity\"\n element={gated('workspaces.activity', <ActivityListPage />)}\n />\n\n {/* Catch-all redirect - uses absolute path to prevent infinite loops */}\n <Route path=\"*\" element={<CatchAllRedirect />} />\n </Routes>\n </Suspense>\n );\n}\n"],"mappings":";;;;;;;AASA,IAAM,IAAsB,QAC1B,OAAO,kCAA+B,MAAM,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,CACxF,EACK,IAAsB,QAC1B,OAAO,kCAA+B,MAAM,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,CACxF,EACK,IAAwB,QAC5B,OAAO,oCAAiC,MAAM,OAAO,EAAE,SAAS,EAAE,uBAAuB,EAAE,CAC5F,EACK,IAAmB,QACvB,OAAO,+BAA4B,MAAM,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAClF,EACK,IAAoB,QACxB,OAAO,gCAA6B,MAAM,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,CACpF,EACK,IAAkB,QACtB,OAAO,8BAA2B,MAAM,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,CAChF,EACK,IAAmB,QACvB,OAAO,+BAA4B,MAAM,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAClF,EACK,IAAuB,QAC3B,OAAO,mCAAgC,MAAM,OAAO,EAAE,SAAS,EAAE,sBAAsB,EAAE,CAC1F,EACK,IAAqB,QACzB,OAAO,iCAA8B,MAAM,OAAO,EAAE,SAAS,EAAE,oBAAoB,EAAE,CACtF;AAKD,SAAS,IAAa;AACpB,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD,EAAK,WAAU,8DAA+D,CAAA;EAC1E,CAAA;;AAOV,SAAS,IAA0B;AACjC,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;GAAI,WAAU;aAA+C;GAA0B,CAAA,EACvF,kBAAC,KAAD;GAAG,WAAU;aAA8B;GAGvC,CAAA,CACA,EAAA,CAAA;EACF,CAAA;;AAQV,SAAS,EAAM,GAAc,GAA0B,IAAe,IAA2B;AAC/F,QACE,kBAAC,GAAD;EAAmB;EAAoB;EAAc,UAAU,kBAAC,GAAD,EAA2B,CAAA;YACvF;EACW,CAAA;;AAQlB,SAAS,IAAmB;CAC1B,IAAM,EAAE,cAAW,kBAAkB,GAAsB;AAC3D,QAAO,kBAAC,GAAD;EAAU,IAAI,GAAG,EAAS;EAAQ,SAAA;EAAU,CAAA;;
|
|
1
|
+
{"version":3,"file":"WorkspacesRoutes.js","names":[],"sources":["../src/WorkspacesRoutes.tsx"],"sourcesContent":["import { lazy, Suspense } from 'react';\nimport { Routes, Route, Navigate } from 'react-router-dom';\nimport { FeatureGate } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { useWorkspacesContext } from './providers/WorkspacesProvider';\n\n// Eager load - main entry point\nimport { WorkspacesListPage } from './pages/WorkspacesListPage';\n\n// Lazy load - secondary pages\nconst CreateWorkspacePage = lazy(() =>\n import('./pages/CreateWorkspacePage').then((m) => ({ default: m.CreateWorkspacePage }))\n);\nconst WorkspaceDetailPage = lazy(() =>\n import('./pages/WorkspaceDetailPage').then((m) => ({ default: m.WorkspaceDetailPage }))\n);\nconst WorkspaceSettingsPage = lazy(() =>\n import('./pages/WorkspaceSettingsPage').then((m) => ({ default: m.WorkspaceSettingsPage }))\n);\nconst ProjectsListPage = lazy(() =>\n import('./pages/ProjectsListPage').then((m) => ({ default: m.ProjectsListPage }))\n);\nconst ProjectDetailPage = lazy(() =>\n import('./pages/ProjectDetailPage').then((m) => ({ default: m.ProjectDetailPage }))\n);\nconst ProjectMembersPage = lazy(() =>\n import('./pages/ProjectMembersPage').then((m) => ({ default: m.ProjectMembersPage }))\n);\nconst MembersListPage = lazy(() =>\n import('./pages/MembersListPage').then((m) => ({ default: m.MembersListPage }))\n);\nconst ActivityListPage = lazy(() =>\n import('./pages/ActivityListPage').then((m) => ({ default: m.ActivityListPage }))\n);\nconst InviteAcceptancePage = lazy(() =>\n import('./pages/InviteAcceptancePage').then((m) => ({ default: m.InviteAcceptancePage }))\n);\nconst PendingInvitesPage = lazy(() =>\n import('./pages/PendingInvitesPage').then((m) => ({ default: m.PendingInvitesPage }))\n);\nconst WorkspaceInvitePage = lazy(() =>\n import('./pages/WorkspaceInvitePage').then((m) => ({ default: m.WorkspaceInvitePage }))\n);\n\n/**\n * Loading fallback for lazy-loaded components\n */\nfunction PageLoader() {\n return (\n <div className=\"flex items-center justify-center h-64\">\n <div className=\"animate-spin rounded-full size-8 border-b-2 border-primary\" />\n </div>\n );\n}\n\n/**\n * Fallback shown when a feature-gated route is disabled.\n */\nfunction FeatureDisabledFallback() {\n return (\n <div className=\"flex h-full items-center justify-center p-8 text-center\">\n <div>\n <h2 className=\"text-lg font-semibold mb-2 text-text-primary\">Feature Not Available</h2>\n <p className=\"text-sm text-text-secondary\">\n This feature is not currently available. Enable it under Feature Previews to access this\n functionality.\n </p>\n </div>\n </div>\n );\n}\n\n/**\n * Helper: wraps a page element in a FeatureGate.\n * defaultValue=false → hidden until explicitly enabled (unreleased feature).\n */\nfunction gated(flag: string, element: React.ReactNode, defaultValue = false): React.ReactElement {\n return (\n <FeatureGate flag={flag} defaultValue={defaultValue} fallback={<FeatureDisabledFallback />}>\n {element}\n </FeatureGate>\n );\n}\n\n/**\n * Catch-all redirect component that uses the basePath from context\n * to navigate to an absolute path, preventing infinite redirect loops.\n */\nfunction CatchAllRedirect() {\n const { basePath = '/workspaces' } = useWorkspacesContext();\n return <Navigate to={`${basePath}/list`} replace />;\n}\n\nfunction CurrentWorkspaceInviteRedirect() {\n const { basePath = '/workspaces', workspaceId } = useWorkspacesContext();\n\n if (!workspaceId) {\n return <WorkspaceInvitePage />;\n }\n\n return <Navigate to={`${basePath}/${workspaceId}/invite`} replace />;\n}\n\n/**\n * Internal routing for the Workspaces microfrontend.\n * Defines all routes and their corresponding page components.\n *\n * - WorkspacesListPage: Eager loaded (main entry point)\n * - All other pages: Lazy loaded for better performance\n * - Projects, Members, Activity: gated by feature flags (unreleased)\n */\nexport function WorkspacesRoutes() {\n return (\n <Suspense fallback={<PageLoader />}>\n <Routes>\n {/* Default redirect */}\n <Route index element={<Navigate to=\"list\" replace />} />\n\n {/* Workspaces List - Eager loaded */}\n <Route path=\"list\" element={<WorkspacesListPage />} />\n\n {/* Create Workspace (must come before :workspaceId to avoid conflicts) */}\n <Route path=\"new\" element={<CreateWorkspacePage />} />\n\n {/* Pending Invites (must come before :workspaceId to avoid conflicts) */}\n <Route path=\"invites\" element={<PendingInvitesPage />} />\n\n {/* Workspace Invite */}\n <Route path=\"invite\" element={<CurrentWorkspaceInviteRedirect />} />\n\n {/* Invitation Acceptance (must come before :workspaceId to avoid conflicts) */}\n <Route path=\"invite/:invitationId\" element={<InviteAcceptancePage />} />\n\n {/* Workspace Detail */}\n <Route path=\":workspaceId\" element={<WorkspaceDetailPage />} />\n <Route\n path=\":workspaceId/settings\"\n element={gated('workspaces.settings', <WorkspaceSettingsPage />, true)}\n />\n <Route path=\":workspaceId/invite\" element={<WorkspaceInvitePage />} />\n\n {/* Projects — core feature, never gated */}\n <Route path=\"projects\" element={<ProjectsListPage />} />\n <Route path=\":workspaceId/projects\" element={<ProjectsListPage />} />\n <Route path=\":workspaceId/projects/:projectId\" element={<ProjectDetailPage />} />\n <Route\n path=\":workspaceId/projects/:projectId/members\"\n element={gated('workspaces.members', <ProjectMembersPage />, true)}\n />\n\n {/* Members — gated by workspaces.members */}\n <Route path=\"members\" element={gated('workspaces.members', <MembersListPage />, true)} />\n <Route\n path=\":workspaceId/members\"\n element={gated('workspaces.members', <MembersListPage />, true)}\n />\n\n {/* Activity — gated by workspaces.activity */}\n <Route path=\"activity\" element={gated('workspaces.activity', <ActivityListPage />)} />\n <Route\n path=\":workspaceId/activity\"\n element={gated('workspaces.activity', <ActivityListPage />)}\n />\n\n {/* Catch-all redirect - uses absolute path to prevent infinite loops */}\n <Route path=\"*\" element={<CatchAllRedirect />} />\n </Routes>\n </Suspense>\n );\n}\n"],"mappings":";;;;;;;AASA,IAAM,IAAsB,QAC1B,OAAO,kCAA+B,MAAM,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,CACxF,EACK,IAAsB,QAC1B,OAAO,kCAA+B,MAAM,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,CACxF,EACK,IAAwB,QAC5B,OAAO,oCAAiC,MAAM,OAAO,EAAE,SAAS,EAAE,uBAAuB,EAAE,CAC5F,EACK,IAAmB,QACvB,OAAO,+BAA4B,MAAM,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAClF,EACK,IAAoB,QACxB,OAAO,gCAA6B,MAAM,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,CACpF,EACK,IAAqB,QACzB,OAAO,iCAA8B,MAAM,OAAO,EAAE,SAAS,EAAE,oBAAoB,EAAE,CACtF,EACK,IAAkB,QACtB,OAAO,8BAA2B,MAAM,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,CAChF,EACK,IAAmB,QACvB,OAAO,+BAA4B,MAAM,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAClF,EACK,IAAuB,QAC3B,OAAO,mCAAgC,MAAM,OAAO,EAAE,SAAS,EAAE,sBAAsB,EAAE,CAC1F,EACK,IAAqB,QACzB,OAAO,iCAA8B,MAAM,OAAO,EAAE,SAAS,EAAE,oBAAoB,EAAE,CACtF,EACK,IAAsB,QAC1B,OAAO,kCAA+B,MAAM,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,CACxF;AAKD,SAAS,IAAa;AACpB,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD,EAAK,WAAU,8DAA+D,CAAA;EAC1E,CAAA;;AAOV,SAAS,IAA0B;AACjC,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;GAAI,WAAU;aAA+C;GAA0B,CAAA,EACvF,kBAAC,KAAD;GAAG,WAAU;aAA8B;GAGvC,CAAA,CACA,EAAA,CAAA;EACF,CAAA;;AAQV,SAAS,EAAM,GAAc,GAA0B,IAAe,IAA2B;AAC/F,QACE,kBAAC,GAAD;EAAmB;EAAoB;EAAc,UAAU,kBAAC,GAAD,EAA2B,CAAA;YACvF;EACW,CAAA;;AAQlB,SAAS,IAAmB;CAC1B,IAAM,EAAE,cAAW,kBAAkB,GAAsB;AAC3D,QAAO,kBAAC,GAAD;EAAU,IAAI,GAAG,EAAS;EAAQ,SAAA;EAAU,CAAA;;AAGrD,SAAS,IAAiC;CACxC,IAAM,EAAE,cAAW,eAAe,mBAAgB,GAAsB;AAMxE,QAJK,IAIE,kBAAC,GAAD;EAAU,IAAI,GAAG,EAAS,GAAG,EAAY;EAAU,SAAA;EAAU,CAAA,GAH3D,kBAAC,GAAD,EAAuB,CAAA;;AAclC,SAAgB,IAAmB;AACjC,QACE,kBAAC,GAAD;EAAU,UAAU,kBAAC,GAAD,EAAc,CAAA;YAChC,kBAAC,GAAD,EAAA,UAAA;GAEE,kBAAC,GAAD;IAAO,OAAA;IAAM,SAAS,kBAAC,GAAD;KAAU,IAAG;KAAO,SAAA;KAAU,CAAA;IAAI,CAAA;GAGxD,kBAAC,GAAD;IAAO,MAAK;IAAO,SAAS,kBAAC,GAAD,EAAsB,CAAA;IAAI,CAAA;GAGtD,kBAAC,GAAD;IAAO,MAAK;IAAM,SAAS,kBAAC,GAAD,EAAuB,CAAA;IAAI,CAAA;GAGtD,kBAAC,GAAD;IAAO,MAAK;IAAU,SAAS,kBAAC,GAAD,EAAsB,CAAA;IAAI,CAAA;GAGzD,kBAAC,GAAD;IAAO,MAAK;IAAS,SAAS,kBAAC,GAAD,EAAkC,CAAA;IAAI,CAAA;GAGpE,kBAAC,GAAD;IAAO,MAAK;IAAuB,SAAS,kBAAC,GAAD,EAAwB,CAAA;IAAI,CAAA;GAGxE,kBAAC,GAAD;IAAO,MAAK;IAAe,SAAS,kBAAC,GAAD,EAAuB,CAAA;IAAI,CAAA;GAC/D,kBAAC,GAAD;IACE,MAAK;IACL,SAAS,EAAM,uBAAuB,kBAAC,GAAD,EAAyB,CAAA,EAAE,GAAK;IACtE,CAAA;GACF,kBAAC,GAAD;IAAO,MAAK;IAAsB,SAAS,kBAAC,GAAD,EAAuB,CAAA;IAAI,CAAA;GAGtE,kBAAC,GAAD;IAAO,MAAK;IAAW,SAAS,kBAAC,GAAD,EAAoB,CAAA;IAAI,CAAA;GACxD,kBAAC,GAAD;IAAO,MAAK;IAAwB,SAAS,kBAAC,GAAD,EAAoB,CAAA;IAAI,CAAA;GACrE,kBAAC,GAAD;IAAO,MAAK;IAAmC,SAAS,kBAAC,GAAD,EAAqB,CAAA;IAAI,CAAA;GACjF,kBAAC,GAAD;IACE,MAAK;IACL,SAAS,EAAM,sBAAsB,kBAAC,GAAD,EAAsB,CAAA,EAAE,GAAK;IAClE,CAAA;GAGF,kBAAC,GAAD;IAAO,MAAK;IAAU,SAAS,EAAM,sBAAsB,kBAAC,GAAD,EAAmB,CAAA,EAAE,GAAK;IAAI,CAAA;GACzF,kBAAC,GAAD;IACE,MAAK;IACL,SAAS,EAAM,sBAAsB,kBAAC,GAAD,EAAmB,CAAA,EAAE,GAAK;IAC/D,CAAA;GAGF,kBAAC,GAAD;IAAO,MAAK;IAAW,SAAS,EAAM,uBAAuB,kBAAC,GAAD,EAAoB,CAAA,CAAC;IAAI,CAAA;GACtF,kBAAC,GAAD;IACE,MAAK;IACL,SAAS,EAAM,uBAAuB,kBAAC,GAAD,EAAoB,CAAA,CAAC;IAC3D,CAAA;GAGF,kBAAC,GAAD;IAAO,MAAK;IAAI,SAAS,kBAAC,GAAD,EAAoB,CAAA;IAAI,CAAA;GAC1C,EAAA,CAAA;EACA,CAAA"}
|
|
@@ -1,148 +1,235 @@
|
|
|
1
1
|
import { useWorkspacesStore as e } from "../store/workspacesStore.js";
|
|
2
2
|
import { useCreateInvitation as t } from "../hooks/useMemberMutations.js";
|
|
3
3
|
import { useWorkspaceRoles as n } from "../hooks/useWorkspaceRoles.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
4
|
+
import { useProjects as r } from "../hooks/useProjects.js";
|
|
5
|
+
import { useState as i } from "react";
|
|
6
|
+
import { AlertCircle as a } from "lucide-react";
|
|
7
|
+
import { jsx as o, jsxs as s } from "react/jsx-runtime";
|
|
8
|
+
import { Badge as c, Button as l, Checkbox as u, ResponsiveDialog as d, ResponsiveDialogContent as f, ResponsiveDialogDescription as p, ResponsiveDialogHeader as m, ResponsiveDialogTitle as h, Tooltip as g, TooltipContent as _, TooltipProvider as v, TooltipTrigger as y } from "@burdenoff/fe-libs/ui";
|
|
9
|
+
import { FormControl as b, FormDescription as x, FormField as S, FormItem as C, FormLabel as w, FormMessage as T, FormProvider as E, Input as D, useForm as O, z as k } from "@burdenoff/fe-libs/form";
|
|
9
10
|
//#region src/components/InviteMemberDialog.tsx
|
|
10
|
-
var
|
|
11
|
-
email:
|
|
12
|
-
roleIds:
|
|
11
|
+
var A = k.object({
|
|
12
|
+
email: k.string().email("Invalid email address"),
|
|
13
|
+
roleIds: k.array(k.string()),
|
|
14
|
+
projectIds: k.array(k.string())
|
|
13
15
|
});
|
|
14
|
-
function
|
|
15
|
-
let { isInviteMemberDialogOpen:
|
|
16
|
-
schema:
|
|
16
|
+
function j({ workspaceId: k }) {
|
|
17
|
+
let { isInviteMemberDialogOpen: j, closeInviteMemberDialog: M } = e(), { mutate: N, isPending: P } = t(k), { data: F = [], isLoading: I } = n(k), { data: L, isLoading: R } = r({ archived: !1 }, { limit: 100 }, k), [z, B] = i(null), V = L?.items ?? [], H = O({
|
|
18
|
+
schema: A,
|
|
17
19
|
defaultValues: {
|
|
18
20
|
email: "",
|
|
19
|
-
roleIds: []
|
|
21
|
+
roleIds: [],
|
|
22
|
+
projectIds: []
|
|
20
23
|
}
|
|
21
|
-
}),
|
|
22
|
-
if (
|
|
23
|
-
|
|
24
|
+
}), U = (e) => {
|
|
25
|
+
if (B(null), !k) {
|
|
26
|
+
B("No workspace selected. Please select a workspace first.");
|
|
24
27
|
return;
|
|
25
28
|
}
|
|
26
|
-
|
|
29
|
+
N({
|
|
27
30
|
email: e.email,
|
|
28
31
|
roleIds: e.roleIds,
|
|
29
|
-
workspaceId:
|
|
32
|
+
workspaceId: k,
|
|
33
|
+
...e.projectIds.length > 0 ? { context: {
|
|
34
|
+
type: "workspace.invitation",
|
|
35
|
+
metadata: { projectIds: e.projectIds }
|
|
36
|
+
} } : {}
|
|
30
37
|
}, {
|
|
31
38
|
onSuccess: () => {
|
|
32
|
-
|
|
39
|
+
M(), H.reset(), B(null);
|
|
33
40
|
},
|
|
34
41
|
onError: (e) => {
|
|
35
42
|
let t = e instanceof Error ? e.message : "Failed to send invitation";
|
|
36
|
-
console.error("Failed to create invitation:", e),
|
|
43
|
+
console.error("Failed to create invitation:", e), B(t);
|
|
37
44
|
}
|
|
38
45
|
});
|
|
39
|
-
},
|
|
40
|
-
e || (
|
|
46
|
+
}, W = (e) => {
|
|
47
|
+
e || (M(), H.reset(), B(null));
|
|
41
48
|
};
|
|
42
|
-
return /* @__PURE__ */
|
|
43
|
-
open:
|
|
44
|
-
onOpenChange:
|
|
45
|
-
children: /* @__PURE__ */
|
|
46
|
-
className: "sm:max-w-[
|
|
47
|
-
children: [/* @__PURE__ */
|
|
48
|
-
...
|
|
49
|
-
children: /* @__PURE__ */
|
|
50
|
-
onSubmit:
|
|
51
|
-
className: "
|
|
52
|
-
children: [
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
render: ({ field: e }) => /* @__PURE__ */ o(_, { children: [
|
|
61
|
-
/* @__PURE__ */ a(v, { children: "Email Address *" }),
|
|
62
|
-
/* @__PURE__ */ a(m, { children: /* @__PURE__ */ a(x, {
|
|
63
|
-
type: "email",
|
|
64
|
-
placeholder: "member@example.com",
|
|
65
|
-
...e,
|
|
66
|
-
disabled: O,
|
|
67
|
-
onChange: (t) => {
|
|
68
|
-
e.onChange(t), M(null);
|
|
69
|
-
}
|
|
70
|
-
}) }),
|
|
71
|
-
/* @__PURE__ */ a(h, { children: "Enter the email address of the person you want to invite" }),
|
|
72
|
-
/* @__PURE__ */ a(y, {})
|
|
73
|
-
] })
|
|
74
|
-
}),
|
|
75
|
-
/* @__PURE__ */ a(g, {
|
|
76
|
-
control: N.control,
|
|
77
|
-
name: "roleIds",
|
|
78
|
-
render: ({ field: e }) => /* @__PURE__ */ o(_, { children: [
|
|
79
|
-
/* @__PURE__ */ o("div", {
|
|
80
|
-
className: "flex items-center justify-between gap-2",
|
|
81
|
-
children: [/* @__PURE__ */ a(v, { children: "Roles (Optional)" }), e.value.length > 0 && /* @__PURE__ */ o(s, {
|
|
82
|
-
variant: "secondary",
|
|
83
|
-
children: [e.value.length, " selected"]
|
|
84
|
-
})]
|
|
49
|
+
return /* @__PURE__ */ o(d, {
|
|
50
|
+
open: j,
|
|
51
|
+
onOpenChange: W,
|
|
52
|
+
children: /* @__PURE__ */ s(f, {
|
|
53
|
+
className: "z-[120] flex h-[min(88vh,900px)] flex-col overflow-hidden sm:max-w-[960px] lg:max-w-[1120px]",
|
|
54
|
+
children: [/* @__PURE__ */ s(m, { children: [/* @__PURE__ */ o(h, { children: "Invite Member" }), /* @__PURE__ */ o(p, { children: "Invite a new member to this workspace by email. They'll receive an invitation link to join." })] }), /* @__PURE__ */ o(E, {
|
|
55
|
+
...H,
|
|
56
|
+
children: /* @__PURE__ */ s("form", {
|
|
57
|
+
onSubmit: H.handleSubmit(U),
|
|
58
|
+
className: "flex min-h-0 flex-1 flex-col",
|
|
59
|
+
children: [/* @__PURE__ */ o("div", {
|
|
60
|
+
className: "min-h-0 flex-1 overflow-y-auto pr-2",
|
|
61
|
+
children: /* @__PURE__ */ s("div", {
|
|
62
|
+
className: "space-y-5 pb-4 pr-2",
|
|
63
|
+
children: [
|
|
64
|
+
z && /* @__PURE__ */ s("div", {
|
|
65
|
+
className: "flex items-center gap-2 rounded-md border border-destructive/20 bg-destructive/10 p-3 text-sm text-destructive",
|
|
66
|
+
children: [/* @__PURE__ */ o(a, { className: "size-4 flex-shrink-0" }), /* @__PURE__ */ o("span", { children: z })]
|
|
85
67
|
}),
|
|
86
|
-
/* @__PURE__ */
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
children: "
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
68
|
+
/* @__PURE__ */ o(S, {
|
|
69
|
+
control: H.control,
|
|
70
|
+
name: "email",
|
|
71
|
+
render: ({ field: e }) => /* @__PURE__ */ s(C, { children: [
|
|
72
|
+
/* @__PURE__ */ o(w, { children: "Email Address *" }),
|
|
73
|
+
/* @__PURE__ */ o(b, { children: /* @__PURE__ */ o(D, {
|
|
74
|
+
type: "email",
|
|
75
|
+
placeholder: "member@example.com",
|
|
76
|
+
...e,
|
|
77
|
+
disabled: P,
|
|
78
|
+
onChange: (t) => {
|
|
79
|
+
e.onChange(t), B(null);
|
|
80
|
+
}
|
|
81
|
+
}) }),
|
|
82
|
+
/* @__PURE__ */ o(x, { children: "Enter the email address of the person you want to invite" }),
|
|
83
|
+
/* @__PURE__ */ o(T, {})
|
|
84
|
+
] })
|
|
85
|
+
}),
|
|
86
|
+
/* @__PURE__ */ o(S, {
|
|
87
|
+
control: H.control,
|
|
88
|
+
name: "roleIds",
|
|
89
|
+
render: ({ field: e }) => /* @__PURE__ */ s(C, { children: [
|
|
90
|
+
/* @__PURE__ */ s("div", {
|
|
91
|
+
className: "flex flex-wrap items-center justify-between gap-2",
|
|
92
|
+
children: [/* @__PURE__ */ s("div", {
|
|
93
|
+
className: "space-y-1",
|
|
94
|
+
children: [/* @__PURE__ */ o(w, { children: "Roles (Optional)" }), /* @__PURE__ */ o("p", {
|
|
95
|
+
className: "text-sm text-muted-foreground",
|
|
96
|
+
children: "Choose roles. Hover any role to preview its full description."
|
|
97
|
+
})]
|
|
98
|
+
}), e.value.length > 0 ? /* @__PURE__ */ s(c, {
|
|
99
|
+
variant: "secondary",
|
|
100
|
+
children: [e.value.length, " selected"]
|
|
101
|
+
}) : null]
|
|
102
|
+
}),
|
|
103
|
+
/* @__PURE__ */ o(b, { children: /* @__PURE__ */ o(v, { children: /* @__PURE__ */ o("div", {
|
|
104
|
+
className: "grid grid-cols-1 gap-2 md:grid-cols-2 xl:grid-cols-3",
|
|
105
|
+
children: I ? /* @__PURE__ */ o("p", {
|
|
106
|
+
className: "text-sm text-muted-foreground",
|
|
107
|
+
children: "Loading roles…"
|
|
108
|
+
}) : F.length === 0 ? /* @__PURE__ */ o("p", {
|
|
109
|
+
className: "text-sm text-muted-foreground",
|
|
110
|
+
children: "No assignable roles were returned for this workspace."
|
|
111
|
+
}) : F.map((t) => {
|
|
112
|
+
let n = e.value.includes(t.id);
|
|
113
|
+
return /* @__PURE__ */ s(g, { children: [/* @__PURE__ */ o(y, {
|
|
114
|
+
asChild: !0,
|
|
115
|
+
children: /* @__PURE__ */ s("label", {
|
|
116
|
+
className: `flex cursor-pointer items-center justify-between gap-3 rounded-lg border px-3 py-3 text-left text-sm font-medium transition-colors focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2 ${n ? "border-primary bg-primary/10 text-foreground shadow-sm" : "border-border bg-background text-foreground hover:bg-accent"}`,
|
|
117
|
+
children: [/* @__PURE__ */ o("span", {
|
|
118
|
+
className: "min-w-0 flex-1 truncate",
|
|
119
|
+
children: t.name
|
|
120
|
+
}), /* @__PURE__ */ o(u, {
|
|
121
|
+
checked: n,
|
|
122
|
+
disabled: P,
|
|
123
|
+
onCheckedChange: (n) => {
|
|
124
|
+
let r = n ? [...e.value, t.id] : e.value.filter((e) => e !== t.id);
|
|
125
|
+
e.onChange(r);
|
|
126
|
+
},
|
|
127
|
+
"aria-label": `Select ${t.name}`
|
|
128
|
+
})]
|
|
129
|
+
})
|
|
130
|
+
}), /* @__PURE__ */ s(_, {
|
|
131
|
+
className: "max-w-xs text-sm leading-6",
|
|
132
|
+
children: [
|
|
133
|
+
/* @__PURE__ */ o("p", {
|
|
134
|
+
className: "font-medium",
|
|
135
|
+
children: t.name
|
|
136
|
+
}),
|
|
137
|
+
/* @__PURE__ */ o("p", {
|
|
138
|
+
className: "mt-1 text-muted-foreground",
|
|
139
|
+
children: t.description || "No description available for this role."
|
|
140
|
+
}),
|
|
141
|
+
t.isSystem ? /* @__PURE__ */ o("p", {
|
|
142
|
+
className: "mt-2 text-xs",
|
|
143
|
+
children: "System role"
|
|
144
|
+
}) : null
|
|
145
|
+
]
|
|
146
|
+
})] }, t.id);
|
|
147
|
+
})
|
|
148
|
+
}) }) }),
|
|
149
|
+
/* @__PURE__ */ o(T, {})
|
|
150
|
+
] })
|
|
151
|
+
}),
|
|
152
|
+
/* @__PURE__ */ o(S, {
|
|
153
|
+
control: H.control,
|
|
154
|
+
name: "projectIds",
|
|
155
|
+
render: ({ field: e }) => /* @__PURE__ */ s(C, { children: [
|
|
156
|
+
/* @__PURE__ */ s("div", {
|
|
157
|
+
className: "flex flex-wrap items-center justify-between gap-2",
|
|
158
|
+
children: [/* @__PURE__ */ s("div", {
|
|
159
|
+
className: "space-y-1",
|
|
160
|
+
children: [/* @__PURE__ */ o(w, { children: "Projects to add on acceptance (Optional)" }), /* @__PURE__ */ o("p", {
|
|
161
|
+
className: "text-sm text-muted-foreground",
|
|
162
|
+
children: "The invited user will be added to these projects automatically after they accept the workspace invitation."
|
|
113
163
|
})]
|
|
114
|
-
}),
|
|
115
|
-
|
|
116
|
-
children:
|
|
117
|
-
})]
|
|
118
|
-
},
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
164
|
+
}), e.value.length > 0 ? /* @__PURE__ */ s(c, {
|
|
165
|
+
variant: "secondary",
|
|
166
|
+
children: [e.value.length, " selected"]
|
|
167
|
+
}) : null]
|
|
168
|
+
}),
|
|
169
|
+
/* @__PURE__ */ o(b, { children: /* @__PURE__ */ o("div", {
|
|
170
|
+
className: "grid grid-cols-1 gap-2 md:grid-cols-2 xl:grid-cols-3",
|
|
171
|
+
children: R ? /* @__PURE__ */ o("p", {
|
|
172
|
+
className: "text-sm text-muted-foreground",
|
|
173
|
+
children: "Loading projects…"
|
|
174
|
+
}) : V.length === 0 ? /* @__PURE__ */ o("p", {
|
|
175
|
+
className: "text-sm text-muted-foreground",
|
|
176
|
+
children: "No active projects are available in this workspace yet."
|
|
177
|
+
}) : V.map((t) => {
|
|
178
|
+
let n = e.value.includes(t.id);
|
|
179
|
+
return /* @__PURE__ */ s("label", {
|
|
180
|
+
className: `flex cursor-pointer items-start justify-between gap-3 rounded-lg border px-3 py-3 transition-colors focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2 ${n ? "border-primary bg-primary/10 text-foreground shadow-sm" : "border-border bg-background text-foreground hover:bg-accent"}`,
|
|
181
|
+
children: [/* @__PURE__ */ s("div", {
|
|
182
|
+
className: "min-w-0 flex-1 space-y-1",
|
|
183
|
+
children: [/* @__PURE__ */ o("p", {
|
|
184
|
+
className: "truncate text-sm font-semibold leading-tight",
|
|
185
|
+
children: t.name
|
|
186
|
+
}), /* @__PURE__ */ s("div", {
|
|
187
|
+
className: "flex items-center gap-2 text-xs text-muted-foreground",
|
|
188
|
+
children: [/* @__PURE__ */ o("span", {
|
|
189
|
+
className: "truncate font-mono",
|
|
190
|
+
children: t.key
|
|
191
|
+
}), /* @__PURE__ */ o(c, {
|
|
192
|
+
variant: "outline",
|
|
193
|
+
children: t.status
|
|
194
|
+
})]
|
|
195
|
+
})]
|
|
196
|
+
}), /* @__PURE__ */ o(u, {
|
|
197
|
+
checked: n,
|
|
198
|
+
disabled: P,
|
|
199
|
+
onCheckedChange: (n) => {
|
|
200
|
+
let r = n ? [...e.value, t.id] : e.value.filter((e) => e !== t.id);
|
|
201
|
+
e.onChange(r);
|
|
202
|
+
},
|
|
203
|
+
"aria-label": `Select ${t.name}`
|
|
204
|
+
})]
|
|
205
|
+
}, t.id);
|
|
206
|
+
})
|
|
207
|
+
}) }),
|
|
208
|
+
/* @__PURE__ */ o(T, {})
|
|
209
|
+
] })
|
|
210
|
+
})
|
|
211
|
+
]
|
|
138
212
|
})
|
|
139
|
-
|
|
213
|
+
}), /* @__PURE__ */ s("div", {
|
|
214
|
+
className: "flex justify-end gap-3 border-t pt-4",
|
|
215
|
+
children: [/* @__PURE__ */ o(l, {
|
|
216
|
+
type: "button",
|
|
217
|
+
variant: "outline",
|
|
218
|
+
onClick: () => W(!1),
|
|
219
|
+
disabled: P,
|
|
220
|
+
children: "Cancel"
|
|
221
|
+
}), /* @__PURE__ */ o(l, {
|
|
222
|
+
type: "submit",
|
|
223
|
+
disabled: P,
|
|
224
|
+
children: P ? "Sending..." : "Send Invitation"
|
|
225
|
+
})]
|
|
226
|
+
})]
|
|
140
227
|
})
|
|
141
228
|
})]
|
|
142
229
|
})
|
|
143
230
|
});
|
|
144
231
|
}
|
|
145
232
|
//#endregion
|
|
146
|
-
export {
|
|
233
|
+
export { j as InviteMemberDialog };
|
|
147
234
|
|
|
148
235
|
//# sourceMappingURL=InviteMemberDialog.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InviteMemberDialog.js","names":[],"sources":["../../src/components/InviteMemberDialog.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { AlertCircle } from 'lucide-react';\nimport {\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n Button,\n Badge,\n} from '@burdenoff/fe-libs/ui';\nimport {\n FormField,\n FormItem,\n FormLabel,\n FormControl,\n FormMessage,\n FormDescription,\n Input,\n useForm,\n FormProvider,\n z,\n} from '@burdenoff/fe-libs/form';\nimport { useWorkspacesStore } from '../store/workspacesStore';\nimport { useCreateInvitation } from '../hooks/useMemberMutations';\nimport { useWorkspaceRoles } from '../hooks/useWorkspaceRoles';\n\nconst inviteMemberSchema = z.object({\n email: z.string().email('Invalid email address'),\n // Using a lambda default instead of `.default([])` to keep input type required\n // (zod v4 widens defaulted inputs to `T | undefined`, which breaks useForm typing).\n roleIds: z.array(z.string()),\n});\n\ntype InviteMemberFormData = z.infer<typeof inviteMemberSchema>;\n\ninterface InviteMemberDialogProps {\n /** Workspace ID to invite the member to (from URL params) */\n workspaceId?: string;\n}\n\nexport function InviteMemberDialog({ workspaceId }: InviteMemberDialogProps) {\n const { isInviteMemberDialogOpen, closeInviteMemberDialog } = useWorkspacesStore();\n const { mutate: createInvitation, isPending } = useCreateInvitation(workspaceId);\n const { data: availableRoles = [], isLoading: isLoadingRoles } = useWorkspaceRoles(workspaceId);\n const [error, setError] = useState<string | null>(null);\n\n const form = useForm<InviteMemberFormData>({\n schema: inviteMemberSchema,\n defaultValues: {\n email: '',\n roleIds: [],\n },\n });\n\n const onSubmit = (data: InviteMemberFormData) => {\n setError(null);\n\n if (!workspaceId) {\n setError('No workspace selected. Please select a workspace first.');\n return;\n }\n\n createInvitation(\n {\n email: data.email,\n roleIds: data.roleIds,\n workspaceId,\n },\n {\n onSuccess: () => {\n closeInviteMemberDialog();\n form.reset();\n setError(null);\n },\n onError: (err) => {\n const errorMessage = err instanceof Error ? err.message : 'Failed to send invitation';\n console.error('Failed to create invitation:', err);\n setError(errorMessage);\n },\n }\n );\n };\n\n const handleOpenChange = (open: boolean) => {\n if (!open) {\n closeInviteMemberDialog();\n form.reset();\n setError(null);\n }\n };\n\n return (\n <ResponsiveDialog open={isInviteMemberDialogOpen} onOpenChange={handleOpenChange}>\n <ResponsiveDialogContent className=\"sm:max-w-[500px]\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Invite Member</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Invite a new member to this workspace by email. They'll receive an invitation link to\n join.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <FormProvider {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-4\">\n {error && (\n <div className=\"flex items-center gap-2 p-3 text-sm text-destructive bg-destructive/10 border border-destructive/20 rounded-md\">\n <AlertCircle className=\"size-4 flex-shrink-0\" />\n <span>{error}</span>\n </div>\n )}\n\n <FormField\n control={form.control}\n name=\"email\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>Email Address *</FormLabel>\n <FormControl>\n <Input\n type=\"email\"\n placeholder=\"member@example.com\"\n {...field}\n disabled={isPending}\n onChange={(e) => {\n field.onChange(e);\n setError(null); // Clear error when user types\n }}\n />\n </FormControl>\n <FormDescription>\n Enter the email address of the person you want to invite\n </FormDescription>\n <FormMessage />\n </FormItem>\n )}\n />\n\n <FormField\n control={form.control}\n name=\"roleIds\"\n render={({ field }) => (\n <FormItem>\n <div className=\"flex items-center justify-between gap-2\">\n <FormLabel>Roles (Optional)</FormLabel>\n {field.value.length > 0 && (\n <Badge variant=\"secondary\">{field.value.length} selected</Badge>\n )}\n </div>\n <FormControl>\n <div className=\"grid grid-cols-1 gap-2 sm:grid-cols-2\">\n {isLoadingRoles ? (\n <p className=\"text-sm text-muted-foreground\">Loading roles…</p>\n ) : availableRoles.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n No assignable roles were returned for this workspace.\n </p>\n ) : (\n availableRoles.map((role) => {\n const isSelected = field.value.includes(role.id);\n return (\n <button\n key={role.id}\n type=\"button\"\n aria-pressed={isSelected}\n disabled={isPending}\n onClick={() => {\n const nextValue = isSelected\n ? field.value.filter((id) => id !== role.id)\n : [...field.value, role.id];\n field.onChange(nextValue);\n }}\n className={`rounded-md border p-3 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ${\n isSelected\n ? 'border-primary bg-primary/10 text-foreground'\n : 'border-border bg-background text-foreground hover:bg-accent'\n }`}\n >\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"font-medium\">{role.name}</span>\n {role.isSystem && <Badge variant=\"outline\">System</Badge>}\n </div>\n {role.description && (\n <p className=\"mt-1 text-xs text-muted-foreground\">\n {role.description}\n </p>\n )}\n </button>\n );\n })\n )}\n </div>\n </FormControl>\n <FormDescription>\n Choose the roles to assign once the invitation is accepted.\n </FormDescription>\n <FormMessage />\n </FormItem>\n )}\n />\n\n <div className=\"flex justify-end gap-3 pt-4\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => handleOpenChange(false)}\n disabled={isPending}\n >\n Cancel\n </Button>\n <Button type=\"submit\" disabled={isPending}>\n {isPending ? 'Sending...' : 'Send Invitation'}\n </Button>\n </div>\n </form>\n </FormProvider>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"InviteMemberDialog.js","names":[],"sources":["../../src/components/InviteMemberDialog.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { AlertCircle } from 'lucide-react';\nimport {\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n Button,\n Badge,\n Checkbox,\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport {\n FormField,\n FormItem,\n FormLabel,\n FormControl,\n FormMessage,\n FormDescription,\n Input,\n useForm,\n FormProvider,\n z,\n} from '@burdenoff/fe-libs/form';\nimport { useWorkspacesStore } from '../store/workspacesStore';\nimport { useCreateInvitation } from '../hooks/useMemberMutations';\nimport { useProjects } from '../hooks/useProjects';\nimport { useWorkspaceRoles } from '../hooks/useWorkspaceRoles';\n\nconst inviteMemberSchema = z.object({\n email: z.string().email('Invalid email address'),\n // Using a lambda default instead of `.default([])` to keep input type required\n // (zod v4 widens defaulted inputs to `T | undefined`, which breaks useForm typing).\n roleIds: z.array(z.string()),\n projectIds: z.array(z.string()),\n});\n\ntype InviteMemberFormData = z.infer<typeof inviteMemberSchema>;\n\ninterface InviteMemberDialogProps {\n /** Workspace ID to invite the member to (from URL params) */\n workspaceId?: string;\n}\n\nexport function InviteMemberDialog({ workspaceId }: InviteMemberDialogProps) {\n const { isInviteMemberDialogOpen, closeInviteMemberDialog } = useWorkspacesStore();\n const { mutate: createInvitation, isPending } = useCreateInvitation(workspaceId);\n const { data: availableRoles = [], isLoading: isLoadingRoles } = useWorkspaceRoles(workspaceId);\n const { data: projectList, isLoading: isLoadingProjects } = useProjects(\n { archived: false },\n { limit: 100 },\n workspaceId\n );\n const [error, setError] = useState<string | null>(null);\n const availableProjects = projectList?.items ?? [];\n\n const form = useForm<InviteMemberFormData>({\n schema: inviteMemberSchema,\n defaultValues: {\n email: '',\n roleIds: [],\n projectIds: [],\n },\n });\n\n const onSubmit = (data: InviteMemberFormData) => {\n setError(null);\n\n if (!workspaceId) {\n setError('No workspace selected. Please select a workspace first.');\n return;\n }\n\n createInvitation(\n {\n email: data.email,\n roleIds: data.roleIds,\n workspaceId,\n ...(data.projectIds.length > 0\n ? {\n context: {\n type: 'workspace.invitation',\n metadata: { projectIds: data.projectIds },\n },\n }\n : {}),\n },\n {\n onSuccess: () => {\n closeInviteMemberDialog();\n form.reset();\n setError(null);\n },\n onError: (err) => {\n const errorMessage = err instanceof Error ? err.message : 'Failed to send invitation';\n console.error('Failed to create invitation:', err);\n setError(errorMessage);\n },\n }\n );\n };\n\n const handleOpenChange = (open: boolean) => {\n if (!open) {\n closeInviteMemberDialog();\n form.reset();\n setError(null);\n }\n };\n\n return (\n <ResponsiveDialog open={isInviteMemberDialogOpen} onOpenChange={handleOpenChange}>\n <ResponsiveDialogContent className=\"z-[120] flex h-[min(88vh,900px)] flex-col overflow-hidden sm:max-w-[960px] lg:max-w-[1120px]\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Invite Member</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Invite a new member to this workspace by email. They'll receive an invitation link to\n join.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <FormProvider {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"flex min-h-0 flex-1 flex-col\">\n <div className=\"min-h-0 flex-1 overflow-y-auto pr-2\">\n <div className=\"space-y-5 pb-4 pr-2\">\n {error && (\n <div className=\"flex items-center gap-2 rounded-md border border-destructive/20 bg-destructive/10 p-3 text-sm text-destructive\">\n <AlertCircle className=\"size-4 flex-shrink-0\" />\n <span>{error}</span>\n </div>\n )}\n\n <FormField\n control={form.control}\n name=\"email\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>Email Address *</FormLabel>\n <FormControl>\n <Input\n type=\"email\"\n placeholder=\"member@example.com\"\n {...field}\n disabled={isPending}\n onChange={(e) => {\n field.onChange(e);\n setError(null);\n }}\n />\n </FormControl>\n <FormDescription>\n Enter the email address of the person you want to invite\n </FormDescription>\n <FormMessage />\n </FormItem>\n )}\n />\n\n <FormField\n control={form.control}\n name=\"roleIds\"\n render={({ field }) => (\n <FormItem>\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <div className=\"space-y-1\">\n <FormLabel>Roles (Optional)</FormLabel>\n <p className=\"text-sm text-muted-foreground\">\n Choose roles. Hover any role to preview its full description.\n </p>\n </div>\n {field.value.length > 0 ? (\n <Badge variant=\"secondary\">{field.value.length} selected</Badge>\n ) : null}\n </div>\n <FormControl>\n <TooltipProvider>\n <div className=\"grid grid-cols-1 gap-2 md:grid-cols-2 xl:grid-cols-3\">\n {isLoadingRoles ? (\n <p className=\"text-sm text-muted-foreground\">Loading roles…</p>\n ) : availableRoles.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n No assignable roles were returned for this workspace.\n </p>\n ) : (\n availableRoles.map((role) => {\n const isSelected = field.value.includes(role.id);\n return (\n <Tooltip key={role.id}>\n <TooltipTrigger asChild>\n <label\n className={`flex cursor-pointer items-center justify-between gap-3 rounded-lg border px-3 py-3 text-left text-sm font-medium transition-colors focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2 ${\n isSelected\n ? 'border-primary bg-primary/10 text-foreground shadow-sm'\n : 'border-border bg-background text-foreground hover:bg-accent'\n }`}\n >\n <span className=\"min-w-0 flex-1 truncate\">{role.name}</span>\n <Checkbox\n checked={isSelected}\n disabled={isPending}\n onCheckedChange={(checked) => {\n const nextValue = checked\n ? [...field.value, role.id]\n : field.value.filter((id) => id !== role.id);\n field.onChange(nextValue);\n }}\n aria-label={`Select ${role.name}`}\n />\n </label>\n </TooltipTrigger>\n <TooltipContent className=\"max-w-xs text-sm leading-6\">\n <p className=\"font-medium\">{role.name}</p>\n <p className=\"mt-1 text-muted-foreground\">\n {role.description ||\n 'No description available for this role.'}\n </p>\n {role.isSystem ? (\n <p className=\"mt-2 text-xs\">System role</p>\n ) : null}\n </TooltipContent>\n </Tooltip>\n );\n })\n )}\n </div>\n </TooltipProvider>\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n\n <FormField\n control={form.control}\n name=\"projectIds\"\n render={({ field }) => (\n <FormItem>\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <div className=\"space-y-1\">\n <FormLabel>Projects to add on acceptance (Optional)</FormLabel>\n <p className=\"text-sm text-muted-foreground\">\n The invited user will be added to these projects automatically after\n they accept the workspace invitation.\n </p>\n </div>\n {field.value.length > 0 ? (\n <Badge variant=\"secondary\">{field.value.length} selected</Badge>\n ) : null}\n </div>\n <FormControl>\n <div className=\"grid grid-cols-1 gap-2 md:grid-cols-2 xl:grid-cols-3\">\n {isLoadingProjects ? (\n <p className=\"text-sm text-muted-foreground\">Loading projects…</p>\n ) : availableProjects.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n No active projects are available in this workspace yet.\n </p>\n ) : (\n availableProjects.map((project) => {\n const isSelected = field.value.includes(project.id);\n\n return (\n <label\n key={project.id}\n className={`flex cursor-pointer items-start justify-between gap-3 rounded-lg border px-3 py-3 transition-colors focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2 ${\n isSelected\n ? 'border-primary bg-primary/10 text-foreground shadow-sm'\n : 'border-border bg-background text-foreground hover:bg-accent'\n }`}\n >\n <div className=\"min-w-0 flex-1 space-y-1\">\n <p className=\"truncate text-sm font-semibold leading-tight\">\n {project.name}\n </p>\n <div className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <span className=\"truncate font-mono\">{project.key}</span>\n <Badge variant=\"outline\">{project.status}</Badge>\n </div>\n </div>\n <Checkbox\n checked={isSelected}\n disabled={isPending}\n onCheckedChange={(checked) => {\n const nextValue = checked\n ? [...field.value, project.id]\n : field.value.filter((id) => id !== project.id);\n field.onChange(nextValue);\n }}\n aria-label={`Select ${project.name}`}\n />\n </label>\n );\n })\n )}\n </div>\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n </div>\n </div>\n\n <div className=\"flex justify-end gap-3 border-t pt-4\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => handleOpenChange(false)}\n disabled={isPending}\n >\n Cancel\n </Button>\n <Button type=\"submit\" disabled={isPending}>\n {isPending ? 'Sending...' : 'Send Invitation'}\n </Button>\n </div>\n </form>\n </FormProvider>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
|
|
@@ -15,7 +15,8 @@ function k({ member: k, selected: A = !1, onToggleSelect: j, canRemove: M = !1 }
|
|
|
15
15
|
}], [k.userId]), {
|
|
16
16
|
apiGatewayUrl: L,
|
|
17
17
|
authToken: R,
|
|
18
|
-
orgId: z
|
|
18
|
+
orgId: z,
|
|
19
|
+
workspaceId: k.workspaceId
|
|
19
20
|
})[k.userId];
|
|
20
21
|
return /* @__PURE__ */ d(l, { children: [/* @__PURE__ */ u(S, {
|
|
21
22
|
className: `hover:bg-bg-canvas transition-all duration-200 ${A ? "ring-2 ring-action-primary-bg/25 border-action-primary-bg/50" : ""}`,
|