@burdenoff/microfe-workspaces 2026.624.2 → 2026.625.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,134 +1,135 @@
1
1
  import { useWorkspacesContext as e } from "./providers/WorkspacesProvider.js";
2
- import { WorkspacesListPage as t } from "./pages/WorkspacesListPage.js";
3
- import { Suspense as n, lazy as r } from "react";
4
- import { FeatureGate as i } from "@burdenoff/fe-libs/shared/feature-flags";
5
- import { jsx as a, jsxs as o } from "react/jsx-runtime";
6
- import { Navigate as s, Route as c, Routes as l } from "react-router-dom";
2
+ import { WorkspaceContextPathSync as t } from "./components/WorkspaceContextPathSync.js";
3
+ import { WorkspacesListPage as n } from "./pages/WorkspacesListPage.js";
4
+ import { Suspense as r, lazy as i } from "react";
5
+ import { FeatureGate as a } from "@burdenoff/fe-libs/shared/feature-flags";
6
+ import { jsx as o, jsxs as s } from "react/jsx-runtime";
7
+ import { Navigate as c, Route as l, Routes as u } from "react-router-dom";
7
8
  //#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/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
- return /* @__PURE__ */ a("div", {
9
+ var d = i(() => import("./pages/CreateWorkspacePage.js").then((e) => ({ default: e.CreateWorkspacePage }))), f = i(() => import("./pages/WorkspaceDetailPage.js").then((e) => ({ default: e.WorkspaceDetailPage }))), p = i(() => import("./pages/WorkspaceSettingsPage.js").then((e) => ({ default: e.WorkspaceSettingsPage }))), m = i(() => import("./pages/ProjectsListPage.js").then((e) => ({ default: e.ProjectsListPage }))), h = i(() => import("./pages/ProjectDetailPage.js").then((e) => ({ default: e.ProjectDetailPage }))), g = i(() => import("./pages/ProjectMembersPage.js").then((e) => ({ default: e.ProjectMembersPage }))), _ = i(() => import("./pages/MembersListPage.js").then((e) => ({ default: e.MembersListPage }))), v = i(() => import("./pages/ActivityListPage.js").then((e) => ({ default: e.ActivityListPage }))), y = i(() => import("./pages/InviteAcceptancePage.js").then((e) => ({ default: e.InviteAcceptancePage }))), b = i(() => import("./pages/PendingInvitesPage.js").then((e) => ({ default: e.PendingInvitesPage }))), x = i(() => import("./pages/WorkspaceInvitePage.js").then((e) => ({ default: e.WorkspaceInvitePage })));
10
+ function S() {
11
+ return /* @__PURE__ */ o("div", {
11
12
  className: "flex items-center justify-center h-64",
12
- children: /* @__PURE__ */ a("div", { className: "animate-spin rounded-full size-8 border-b-2 border-primary" })
13
+ children: /* @__PURE__ */ o("div", { className: "animate-spin rounded-full size-8 border-b-2 border-primary" })
13
14
  });
14
15
  }
15
- function S() {
16
- return /* @__PURE__ */ a("div", {
16
+ function C() {
17
+ return /* @__PURE__ */ o("div", {
17
18
  className: "flex h-full items-center justify-center p-8 text-center",
18
- children: /* @__PURE__ */ o("div", { children: [/* @__PURE__ */ a("h2", {
19
+ children: /* @__PURE__ */ s("div", { children: [/* @__PURE__ */ o("h2", {
19
20
  className: "text-lg font-semibold mb-2 text-text-primary",
20
21
  children: "Feature Not Available"
21
- }), /* @__PURE__ */ a("p", {
22
+ }), /* @__PURE__ */ o("p", {
22
23
  className: "text-sm text-text-secondary",
23
24
  children: "This feature is not currently available. Enable it under Feature Previews to access this functionality."
24
25
  })] })
25
26
  });
26
27
  }
27
- function C(e, t, n = !1) {
28
- return /* @__PURE__ */ a(i, {
28
+ function w(e, t, n = !1) {
29
+ return /* @__PURE__ */ o(a, {
29
30
  flag: e,
30
31
  defaultValue: n,
31
- fallback: /* @__PURE__ */ a(S, {}),
32
+ fallback: /* @__PURE__ */ o(C, {}),
32
33
  children: t
33
34
  });
34
35
  }
35
- function w() {
36
+ function T() {
36
37
  let { basePath: t = "/workspaces" } = e();
37
- return /* @__PURE__ */ a(s, {
38
+ return /* @__PURE__ */ o(c, {
38
39
  to: `${t}/list`,
39
40
  replace: !0
40
41
  });
41
42
  }
42
- function T() {
43
+ function E() {
43
44
  let { basePath: t = "/workspaces", workspaceId: n } = e();
44
- return n ? /* @__PURE__ */ a(s, {
45
+ return n ? /* @__PURE__ */ o(c, {
45
46
  to: `${t}/${n}/invite`,
46
47
  replace: !0
47
- }) : /* @__PURE__ */ a(b, {});
48
+ }) : /* @__PURE__ */ o(x, {});
48
49
  }
49
- function E() {
50
- return /* @__PURE__ */ a(n, {
51
- fallback: /* @__PURE__ */ a(x, {}),
52
- children: /* @__PURE__ */ o(l, { children: [
53
- /* @__PURE__ */ a(c, {
50
+ function D() {
51
+ return /* @__PURE__ */ s(r, {
52
+ fallback: /* @__PURE__ */ o(S, {}),
53
+ children: [/* @__PURE__ */ o(t, {}), /* @__PURE__ */ s(u, { children: [
54
+ /* @__PURE__ */ o(l, {
54
55
  index: !0,
55
- element: /* @__PURE__ */ a(s, {
56
+ element: /* @__PURE__ */ o(c, {
56
57
  to: "list",
57
58
  replace: !0
58
59
  })
59
60
  }),
60
- /* @__PURE__ */ a(c, {
61
+ /* @__PURE__ */ o(l, {
61
62
  path: "list",
62
- element: /* @__PURE__ */ a(t, {})
63
+ element: /* @__PURE__ */ o(n, {})
63
64
  }),
64
- /* @__PURE__ */ a(c, {
65
+ /* @__PURE__ */ o(l, {
65
66
  path: "new",
66
- element: /* @__PURE__ */ a(u, {})
67
+ element: /* @__PURE__ */ o(d, {})
67
68
  }),
68
- /* @__PURE__ */ a(c, {
69
+ /* @__PURE__ */ o(l, {
69
70
  path: "invites",
70
- element: /* @__PURE__ */ a(y, {})
71
+ element: /* @__PURE__ */ o(b, {})
71
72
  }),
72
- /* @__PURE__ */ a(c, {
73
+ /* @__PURE__ */ o(l, {
73
74
  path: "invite",
74
- element: /* @__PURE__ */ a(T, {})
75
+ element: /* @__PURE__ */ o(E, {})
75
76
  }),
76
- /* @__PURE__ */ a(c, {
77
+ /* @__PURE__ */ o(l, {
77
78
  path: "invite/:invitationId",
78
- element: /* @__PURE__ */ a(v, {})
79
+ element: /* @__PURE__ */ o(y, {})
79
80
  }),
80
- /* @__PURE__ */ a(c, {
81
+ /* @__PURE__ */ o(l, {
81
82
  path: ":workspaceId",
82
- element: /* @__PURE__ */ a(d, {})
83
+ element: /* @__PURE__ */ o(f, {})
83
84
  }),
84
- /* @__PURE__ */ a(c, {
85
+ /* @__PURE__ */ o(l, {
85
86
  path: ":workspaceId/settings",
86
- element: C("workspaces.settings", /* @__PURE__ */ a(f, {}), !0)
87
+ element: w("workspaces.settings", /* @__PURE__ */ o(p, {}), !0)
87
88
  }),
88
- /* @__PURE__ */ a(c, {
89
+ /* @__PURE__ */ o(l, {
89
90
  path: ":workspaceId/invite",
90
- element: /* @__PURE__ */ a(b, {})
91
+ element: /* @__PURE__ */ o(x, {})
91
92
  }),
92
- /* @__PURE__ */ a(c, {
93
+ /* @__PURE__ */ o(l, {
93
94
  path: "projects",
94
- element: /* @__PURE__ */ a(p, {})
95
+ element: /* @__PURE__ */ o(m, {})
95
96
  }),
96
- /* @__PURE__ */ a(c, {
97
+ /* @__PURE__ */ o(l, {
97
98
  path: ":workspaceId/projects",
98
- element: /* @__PURE__ */ a(p, {})
99
+ element: /* @__PURE__ */ o(m, {})
99
100
  }),
100
- /* @__PURE__ */ a(c, {
101
+ /* @__PURE__ */ o(l, {
101
102
  path: ":workspaceId/projects/:projectId",
102
- element: /* @__PURE__ */ a(m, {})
103
+ element: /* @__PURE__ */ o(h, {})
103
104
  }),
104
- /* @__PURE__ */ a(c, {
105
+ /* @__PURE__ */ o(l, {
105
106
  path: ":workspaceId/projects/:projectId/members",
106
- element: /* @__PURE__ */ a(h, {})
107
+ element: /* @__PURE__ */ o(g, {})
107
108
  }),
108
- /* @__PURE__ */ a(c, {
109
+ /* @__PURE__ */ o(l, {
109
110
  path: "members",
110
- element: /* @__PURE__ */ a(g, {})
111
+ element: /* @__PURE__ */ o(_, {})
111
112
  }),
112
- /* @__PURE__ */ a(c, {
113
+ /* @__PURE__ */ o(l, {
113
114
  path: ":workspaceId/members",
114
- element: /* @__PURE__ */ a(g, {})
115
+ element: /* @__PURE__ */ o(_, {})
115
116
  }),
116
- /* @__PURE__ */ a(c, {
117
+ /* @__PURE__ */ o(l, {
117
118
  path: "activity",
118
- element: C("workspaces.activity", /* @__PURE__ */ a(_, {}))
119
+ element: w("workspaces.activity", /* @__PURE__ */ o(v, {}))
119
120
  }),
120
- /* @__PURE__ */ a(c, {
121
+ /* @__PURE__ */ o(l, {
121
122
  path: ":workspaceId/activity",
122
- element: C("workspaces.activity", /* @__PURE__ */ a(_, {}))
123
+ element: w("workspaces.activity", /* @__PURE__ */ o(v, {}))
123
124
  }),
124
- /* @__PURE__ */ a(c, {
125
+ /* @__PURE__ */ o(l, {
125
126
  path: "*",
126
- element: /* @__PURE__ */ a(w, {})
127
+ element: /* @__PURE__ */ o(T, {})
127
128
  })
128
- ] })
129
+ ] })]
129
130
  });
130
131
  }
131
132
  //#endregion
132
- export { E as WorkspacesRoutes };
133
+ export { D as WorkspacesRoutes };
133
134
 
134
135
  //# 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 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 path=\":workspaceId/projects/:projectId/members\" element={<ProjectMembersPage />} />\n\n {/* Members — core feature, never gated */}\n <Route path=\"members\" element={<MembersListPage />} />\n <Route path=\":workspaceId/members\" element={<MembersListPage />} />\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":"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"}
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';\nimport { WorkspaceContextPathSync } from './components/WorkspaceContextPathSync';\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 {/* Keeps the scoped `:workspaceId` URL segment in step with the active context\n when the user switches workspace via the chrome context switcher. */}\n <WorkspaceContextPathSync />\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 path=\":workspaceId/projects/:projectId/members\" element={<ProjectMembersPage />} />\n\n {/* Members — core feature, never gated */}\n <Route path=\"members\" element={<MembersListPage />} />\n <Route path=\":workspaceId/members\" element={<MembersListPage />} />\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":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CreateWorkspaceDialog.js","names":[],"sources":["../../src/components/CreateWorkspaceDialog.tsx"],"sourcesContent":["import { useEffect, useMemo } from 'react';\nimport { AlertCircle } from 'lucide-react';\nimport {\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogFooter,\n Button,\n Skeleton,\n} from '@burdenoff/fe-libs/ui';\nimport {\n FormField,\n FormItem,\n FormLabel,\n FormControl,\n FormMessage,\n FormDescription,\n Input,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n useForm,\n FormProvider,\n z,\n} from '@burdenoff/fe-libs/form';\nimport { useWorkspacesStore } from '../store/workspacesStore';\nimport { useCreateWorkspace } from '../hooks/useWorkspaceMutations';\nimport { useMyTenants, useMyOrganizations } from '../hooks/useTenants';\nimport { generateSlug } from '../utils/slug';\n\nconst createWorkspaceSchema = z.object({\n name: z.string().min(2, 'Name must be at least 2 characters').max(100),\n slug: z\n .string()\n .min(2, 'Slug must be at least 2 characters')\n .max(50)\n .regex(/^[a-z0-9-]+$/, 'Slug can only contain lowercase letters, numbers, and hyphens'),\n tenantId: z.string().min(1, 'Please select a tenant'),\n organizationId: z.string().min(1, 'Please select an organization'),\n});\n\ntype CreateWorkspaceFormData = z.infer<typeof createWorkspaceSchema>;\n\nexport function CreateWorkspaceDialog() {\n const { isCreateWorkspaceDialogOpen, closeCreateWorkspaceDialog } = useWorkspacesStore();\n const { mutate: createWorkspace, isPending } = useCreateWorkspace();\n const { data: tenantsData, isLoading: isLoadingTenants, error: tenantsError } = useMyTenants();\n const {\n data: organizationsData,\n isLoading: isLoadingOrganizations,\n error: organizationsError,\n } = useMyOrganizations();\n\n const tenants = useMemo(() => tenantsData?.items ?? [], [tenantsData]);\n const allOrganizations = useMemo(() => organizationsData?.items ?? [], [organizationsData]);\n\n // Prefer the shared tenant (or the only one) — with a single shared tenant\n // there is nothing to choose, so we auto-select it and hide the picker.\n const defaultTenantId = useMemo(() => {\n const sharedTenant = tenants.find(\n (tenant) => tenant.type === 'SHARED' || tenant.slug === 'burdenoff-shared'\n );\n return sharedTenant?.id ?? tenants[0]?.id ?? '';\n }, [tenants]);\n\n const form = useForm<CreateWorkspaceFormData>({\n schema: createWorkspaceSchema,\n defaultValues: {\n name: '',\n slug: '',\n tenantId: '',\n organizationId: '',\n },\n });\n\n const selectedTenantId = form.watch('tenantId');\n const workspaceName = form.watch('name');\n\n // Auto-select the default tenant when the user has no real choice.\n useEffect(() => {\n if (!form.getValues('tenantId') && defaultTenantId) {\n form.setValue('tenantId', defaultTenantId, { shouldValidate: true });\n }\n }, [defaultTenantId, form]);\n\n // Filter organizations by selected tenant\n const filteredOrganizations = useMemo(() => {\n if (!selectedTenantId) return [];\n return allOrganizations.filter((org) => org.tenantId === selectedTenantId);\n }, [selectedTenantId, allOrganizations]);\n\n // Auto-generate slug from name (clear when name is empty)\n useEffect(() => {\n form.setValue('slug', workspaceName ? generateSlug(workspaceName) : '');\n }, [workspaceName, form]);\n\n // Reset organizationId when tenant changes\n useEffect(() => {\n form.setValue('organizationId', '');\n }, [selectedTenantId, form]);\n\n const onSubmit = (data: CreateWorkspaceFormData) => {\n createWorkspace(\n {\n name: data.name,\n slug: data.slug,\n tenantId: data.tenantId,\n organizationId: data.organizationId,\n },\n {\n onSuccess: () => {\n closeCreateWorkspaceDialog();\n form.reset();\n },\n }\n );\n };\n\n return (\n <ResponsiveDialog open={isCreateWorkspaceDialogOpen} onOpenChange={closeCreateWorkspaceDialog}>\n <ResponsiveDialogContent className=\"sm:max-w-[500px]\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Create Workspace</ResponsiveDialogTitle>\n </ResponsiveDialogHeader>\n\n <FormProvider {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-4\">\n {/* Workspace Name */}\n <FormField\n control={form.control}\n name=\"name\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>Workspace Name *</FormLabel>\n <FormControl>\n <Input placeholder=\"My Workspace\" {...field} />\n </FormControl>\n <FormDescription>A descriptive name for your workspace</FormDescription>\n <FormMessage />\n </FormItem>\n )}\n />\n\n {/* Slug */}\n <FormField\n control={form.control}\n name=\"slug\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>Slug *</FormLabel>\n <FormControl>\n <Input placeholder=\"my-workspace\" {...field} />\n </FormControl>\n <FormDescription>URL-friendly identifier (auto-generated)</FormDescription>\n <FormMessage />\n </FormItem>\n )}\n />\n\n {/* Tenant Selection — shown only when the user has more than one\n tenant to choose between. With a single shared tenant it is\n auto-selected and hidden (loading/error/empty still surface). */}\n {tenants.length !== 1 ? (\n <FormField\n control={form.control}\n name=\"tenantId\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>Tenant *</FormLabel>\n {isLoadingTenants ? (\n <Skeleton className=\"h-10 w-full\" />\n ) : tenantsError ? (\n <div className=\"flex items-center gap-2 text-status-error-text text-sm\">\n <AlertCircle size={16} />\n Failed to load tenants\n </div>\n ) : tenants.length === 0 ? (\n <div className=\"text-text-secondary text-sm\">\n No tenants available. Contact your administrator.\n </div>\n ) : (\n <Select onValueChange={field.onChange} value={field.value}>\n <FormControl>\n <SelectTrigger>\n <SelectValue placeholder=\"Select a tenant\" />\n </SelectTrigger>\n </FormControl>\n <SelectContent>\n {tenants.map((tenant) => (\n <SelectItem key={tenant.id} value={tenant.id}>\n {tenant.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n <FormDescription>Select which tenant this workspace belongs to</FormDescription>\n <FormMessage />\n </FormItem>\n )}\n />\n ) : null}\n\n {/* Organization Selection */}\n <FormField\n control={form.control}\n name=\"organizationId\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>Organization *</FormLabel>\n {isLoadingOrganizations ? (\n <Skeleton className=\"h-10 w-full\" />\n ) : organizationsError ? (\n <div className=\"flex items-center gap-2 text-status-error-text text-sm\">\n <AlertCircle size={16} />\n Failed to load organizations\n </div>\n ) : !selectedTenantId ? (\n <div className=\"text-text-secondary text-sm\">\n Please select a tenant first\n </div>\n ) : filteredOrganizations.length === 0 ? (\n <div className=\"text-text-secondary text-sm\">\n No organizations found for this tenant\n </div>\n ) : (\n <Select\n onValueChange={field.onChange}\n value={field.value}\n disabled={!selectedTenantId}\n >\n <FormControl>\n <SelectTrigger>\n <SelectValue placeholder=\"Select an organization\" />\n </SelectTrigger>\n </FormControl>\n <SelectContent>\n {filteredOrganizations.map((org) => (\n <SelectItem key={org.id} value={org.id}>\n {org.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n <FormDescription>Select which organization owns this workspace</FormDescription>\n <FormMessage />\n </FormItem>\n )}\n />\n\n <ResponsiveDialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={closeCreateWorkspaceDialog}\n disabled={isPending}\n >\n Cancel\n </Button>\n <Button\n type=\"submit\"\n disabled={isPending || isLoadingTenants || isLoadingOrganizations}\n >\n {isPending ? 'Creating...' : 'Create Workspace'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </FormProvider>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"CreateWorkspaceDialog.js","names":[],"sources":["../../src/components/CreateWorkspaceDialog.tsx"],"sourcesContent":["import { useEffect, useMemo } from 'react';\nimport { AlertCircle } from 'lucide-react';\nimport {\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogFooter,\n Button,\n Skeleton,\n} from '@burdenoff/fe-libs/ui';\nimport {\n FormField,\n FormItem,\n FormLabel,\n FormControl,\n FormMessage,\n FormDescription,\n Input,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n useForm,\n FormProvider,\n z,\n} from '@burdenoff/fe-libs/form';\nimport { useWorkspacesStore } from '../store/workspacesStore';\nimport { useCreateWorkspace } from '../hooks/useWorkspaceMutations';\nimport { useMyTenants, useMyOrganizations } from '../hooks/useTenants';\nimport { generateSlug } from '../utils/slug';\n\nconst createWorkspaceSchema = z.object({\n name: z.string().min(2, 'Name must be at least 2 characters').max(100),\n slug: z\n .string()\n .min(2, 'Slug must be at least 2 characters')\n .max(50)\n .regex(/^[a-z0-9-]+$/, 'Slug can only contain lowercase letters, numbers, and hyphens'),\n tenantId: z.string().min(1, 'Please select a tenant'),\n organizationId: z.string().min(1, 'Please select an organization'),\n});\n\ntype CreateWorkspaceFormData = z.infer<typeof createWorkspaceSchema>;\n\nexport function CreateWorkspaceDialog() {\n const { isCreateWorkspaceDialogOpen, closeCreateWorkspaceDialog } = useWorkspacesStore();\n const { mutate: createWorkspace, isPending } = useCreateWorkspace();\n const { data: tenantsData, isLoading: isLoadingTenants, error: tenantsError } = useMyTenants();\n const {\n data: organizationsData,\n isLoading: isLoadingOrganizations,\n error: organizationsError,\n } = useMyOrganizations();\n\n const tenants = useMemo(() => tenantsData?.items ?? [], [tenantsData]);\n const allOrganizations = useMemo(() => organizationsData?.items ?? [], [organizationsData]);\n\n // Prefer the shared tenant (or the only one) — with a single shared tenant\n // there is nothing to choose, so we auto-select it and hide the picker.\n const defaultTenantId = useMemo(() => {\n const sharedTenant = tenants.find(\n (tenant) => tenant.type === 'SHARED' || tenant.slug === 'burdenoff-shared'\n );\n return sharedTenant?.id ?? tenants[0]?.id ?? '';\n }, [tenants]);\n\n const form = useForm<CreateWorkspaceFormData>({\n schema: createWorkspaceSchema,\n defaultValues: {\n name: '',\n slug: '',\n tenantId: '',\n organizationId: '',\n },\n });\n\n const selectedTenantId = form.watch('tenantId');\n const workspaceName = form.watch('name');\n\n // Auto-select the default tenant when the user has no real choice.\n useEffect(() => {\n if (!form.getValues('tenantId') && defaultTenantId) {\n form.setValue('tenantId', defaultTenantId, { shouldValidate: true });\n }\n }, [defaultTenantId, form]);\n\n // Filter organizations by selected tenant\n const filteredOrganizations = useMemo(() => {\n if (!selectedTenantId) return [];\n return allOrganizations.filter((org) => org.tenantId === selectedTenantId);\n }, [selectedTenantId, allOrganizations]);\n\n // Auto-generate slug from name (clear when name is empty)\n useEffect(() => {\n form.setValue('slug', workspaceName ? generateSlug(workspaceName) : '');\n }, [workspaceName, form]);\n\n // Reset organizationId when tenant changes\n useEffect(() => {\n form.setValue('organizationId', '');\n }, [selectedTenantId, form]);\n\n const onSubmit = (data: CreateWorkspaceFormData) => {\n createWorkspace(\n {\n name: data.name,\n slug: data.slug,\n tenantId: data.tenantId,\n organizationId: data.organizationId,\n },\n {\n onSuccess: () => {\n closeCreateWorkspaceDialog();\n form.reset();\n },\n }\n );\n };\n\n return (\n <ResponsiveDialog open={isCreateWorkspaceDialogOpen} onOpenChange={closeCreateWorkspaceDialog}>\n <ResponsiveDialogContent className=\"sm:max-w-[500px]\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Create Workspace</ResponsiveDialogTitle>\n </ResponsiveDialogHeader>\n\n <FormProvider {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-4\">\n {/* Workspace Name */}\n <FormField\n control={form.control}\n name=\"name\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>Workspace Name *</FormLabel>\n <FormControl>\n <Input placeholder=\"My Workspace\" {...field} />\n </FormControl>\n <FormDescription>A descriptive name for your workspace</FormDescription>\n <FormMessage />\n </FormItem>\n )}\n />\n\n {/* Slug */}\n <FormField\n control={form.control}\n name=\"slug\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>Slug *</FormLabel>\n <FormControl>\n <Input placeholder=\"my-workspace\" {...field} />\n </FormControl>\n <FormDescription>URL-friendly identifier (auto-generated)</FormDescription>\n <FormMessage />\n </FormItem>\n )}\n />\n\n {/* Tenant Selection — shown only when the user has more than one\n tenant to choose between. With a single shared tenant it is\n auto-selected and hidden (loading/error/empty still surface). */}\n {tenants.length !== 1 ? (\n <FormField\n control={form.control}\n name=\"tenantId\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>Tenant *</FormLabel>\n {isLoadingTenants ? (\n <Skeleton className=\"h-10 w-full\" />\n ) : tenantsError ? (\n <div className=\"flex items-center gap-2 text-status-error-text text-sm\">\n <AlertCircle size={16} />\n Failed to load tenants\n </div>\n ) : tenants.length === 0 ? (\n <div className=\"text-text-secondary text-sm\">\n No tenants available. Contact your administrator.\n </div>\n ) : (\n <Select onValueChange={field.onChange} value={field.value}>\n <FormControl>\n <SelectTrigger>\n <SelectValue placeholder=\"Select a tenant\" />\n </SelectTrigger>\n </FormControl>\n <SelectContent>\n {tenants.map((tenant) => (\n <SelectItem key={tenant.id} value={tenant.id}>\n {tenant.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n <FormDescription>Select which tenant this workspace belongs to</FormDescription>\n <FormMessage />\n </FormItem>\n )}\n />\n ) : null}\n\n {/* Organization Selection */}\n <FormField\n control={form.control}\n name=\"organizationId\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>Organization *</FormLabel>\n {isLoadingOrganizations ? (\n <Skeleton className=\"h-10 w-full\" />\n ) : organizationsError ? (\n <div className=\"flex items-center gap-2 text-status-error-text text-sm\">\n <AlertCircle size={16} />\n Failed to load organizations\n </div>\n ) : !selectedTenantId ? (\n <div className=\"text-text-secondary text-sm\">Please select a tenant first</div>\n ) : filteredOrganizations.length === 0 ? (\n <div className=\"text-text-secondary text-sm\">\n No organizations found for this tenant\n </div>\n ) : (\n <Select\n onValueChange={field.onChange}\n value={field.value}\n disabled={!selectedTenantId}\n >\n <FormControl>\n <SelectTrigger>\n <SelectValue placeholder=\"Select an organization\" />\n </SelectTrigger>\n </FormControl>\n <SelectContent>\n {filteredOrganizations.map((org) => (\n <SelectItem key={org.id} value={org.id}>\n {org.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n <FormDescription>Select which organization owns this workspace</FormDescription>\n <FormMessage />\n </FormItem>\n )}\n />\n\n <ResponsiveDialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={closeCreateWorkspaceDialog}\n disabled={isPending}\n >\n Cancel\n </Button>\n <Button\n type=\"submit\"\n disabled={isPending || isLoadingTenants || isLoadingOrganizations}\n >\n {isPending ? 'Creating...' : 'Create Workspace'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </FormProvider>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectCard.js","names":[],"sources":["../../src/components/ProjectCard.tsx"],"sourcesContent":["import type React from 'react';\nimport { FolderKanban, Archive, MoreVertical, Fingerprint } from 'lucide-react';\nimport { useNavigate } from 'react-router-dom';\nimport {\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Badge,\n Button,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n EntityIdModal,\n useEntityIdModal,\n} from '@burdenoff/fe-libs/ui';\nimport { TagBadge } from '@burdenoff/fe-libs/tag';\n\nimport type { Project, ProjectTag } from '../types';\nimport { formatDate } from '../utils/formatters';\nimport { getProjectDisplayTags } from '../utils/projectTags';\nimport { getProjectStatusClasses } from '../utils/tokenHelpers';\nimport {\n useArchiveProject,\n useUnarchiveProject,\n useDeleteProject,\n} from '../hooks/useProjectMutations';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport { useScopedNavigation } from '../hooks/useScopedNavigation';\nimport { nativeConfirm, nativeImpact } from '../utils/nativeBridge';\n\ninterface ProjectCardProps {\n project: Project;\n selected?: boolean;\n onToggleSelect?: (checked: boolean) => void;\n onTagClick?: (tag: ProjectTag) => void;\n canArchive?: boolean;\n canDelete?: boolean;\n}\n\nexport function ProjectCard({\n project,\n selected = false,\n onToggleSelect,\n onTagClick,\n canArchive = false,\n canDelete = false,\n}: ProjectCardProps) {\n const navigate = useNavigate();\n const { basePath = '/workspaces' } = useWorkspacesContext();\n const { withCurrentSearch } = useScopedNavigation();\n const { mutate: archiveProject } = useArchiveProject(project.workspaceId);\n const { mutate: unarchiveProject } = useUnarchiveProject(project.workspaceId);\n const { mutate: deleteProject } = useDeleteProject(project.workspaceId);\n const idModal = useEntityIdModal();\n const projectTags = getProjectDisplayTags(project);\n\n const handleCardClick = () => {\n navigate(withCurrentSearch(`${basePath}/${project.workspaceId}/projects/${project.id}`));\n };\n\n const handleArchive = (e: React.MouseEvent) => {\n e.stopPropagation();\n if (project.archived) {\n unarchiveProject(project.id);\n } else {\n archiveProject(project.id);\n }\n };\n\n const handleDelete = async (e: React.MouseEvent) => {\n e.stopPropagation();\n void nativeImpact('medium');\n const native = await nativeConfirm({\n title: 'Delete project',\n message: `Are you sure you want to delete \"${project.name}\"?`,\n okButtonTitle: 'Delete',\n cancelButtonTitle: 'Cancel',\n });\n const ok =\n native === null ? confirm(`Are you sure you want to delete \"${project.name}\"?`) : native;\n if (ok) {\n deleteProject(project.id);\n }\n };\n\n const handleCardKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n handleCardClick();\n }\n };\n\n return (\n <>\n <Card\n className={`cursor-pointer relative ${\n selected ? 'ring-2 ring-primary/25 border-primary/50' : ''\n }`}\n onClick={handleCardClick}\n onKeyDown={handleCardKeyDown}\n role=\"button\"\n tabIndex={0}\n aria-label={`Open project ${project.name}`}\n >\n <CardHeader>\n <div className=\"flex items-start justify-between\">\n <div className=\"flex items-center gap-3 flex-1\">\n {onToggleSelect && (\n <input\n type=\"checkbox\"\n checked={selected}\n onChange={(event) => onToggleSelect(event.target.checked)}\n onClick={(event) => event.stopPropagation()}\n aria-label={`Select project ${project.name}`}\n className=\"mt-1 size-4 rounded border-border-subtle text-primary\"\n />\n )}\n <div className=\"p-3 rounded-lg bg-action-primary-bg/10 transition-colors\">\n <FolderKanban className=\"text-primary\" size={24} />\n </div>\n <div className=\"flex-1 min-w-0\">\n <CardTitle className=\"truncate\">{project.name}</CardTitle>\n <p className=\"truncate text-sm text-text-secondary\">{project.key}</p>\n </div>\n </div>\n <DropdownMenu>\n <DropdownMenuTrigger asChild onClick={(e) => e.stopPropagation()}>\n <Button variant=\"ghost\" size=\"sm\" className=\"size-8 p-0\">\n <MoreVertical size={16} />\n <span className=\"sr-only\">Open project actions</span>\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem\n onClick={(e) => {\n e.stopPropagation();\n idModal.show({\n id: project.id,\n name: project.name,\n type: 'Project',\n details: [\n { label: 'Key', value: project.key },\n { label: 'Workspace ID', value: project.workspaceId },\n ],\n });\n }}\n >\n <Fingerprint size={16} className=\"mr-2\" />\n Get ID\n </DropdownMenuItem>\n {canArchive && (\n <DropdownMenuItem onClick={handleArchive}>\n <Archive size={16} className=\"mr-2\" />\n {project.archived ? 'Unarchive' : 'Archive'}\n </DropdownMenuItem>\n )}\n {canDelete && (\n <DropdownMenuItem onClick={handleDelete} className=\"text-status-error-text\">\n Delete\n </DropdownMenuItem>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <div className=\"flex items-center gap-2\">\n <Badge className={`border ${getProjectStatusClasses(project.status)}`}>\n {project.status}\n </Badge>\n {project.archived && (\n <Badge\n variant=\"outline\"\n className=\"border-workspace-archived-border text-workspace-archived-text\"\n >\n <Archive size={12} className=\"mr-1\" />\n Archived\n </Badge>\n )}\n </div>\n <div className=\"text-xs text-text-secondary\">\n Created {formatDate(project.createdAt)}\n </div>\n {projectTags.length > 0 && (\n <div className=\"flex flex-wrap gap-1.5\" aria-label=\"Project tags\">\n {projectTags.slice(0, 4).map((tag) => (\n <span\n key={tag.tagId}\n {...(onTagClick\n ? {\n role: 'button',\n tabIndex: 0,\n onClick: (event: React.MouseEvent<HTMLSpanElement>) => {\n event.stopPropagation();\n onTagClick(tag);\n },\n onKeyDown: (event: React.KeyboardEvent<HTMLSpanElement>) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n event.stopPropagation();\n onTagClick(tag);\n }\n },\n }\n : {})}\n >\n <TagBadge\n id={tag.tagId}\n label={tag.label}\n color={tag.color ?? undefined}\n icon={tag.icon ?? undefined}\n description={tag.description ?? undefined}\n size=\"sm\"\n className={onTagClick ? 'cursor-pointer' : undefined}\n />\n </span>\n ))}\n {projectTags.length > 4 && (\n <Badge variant=\"outline\" className=\"text-xs\">\n +{projectTags.length - 4}\n </Badge>\n )}\n </div>\n )}\n </CardContent>\n </Card>\n <EntityIdModal control={idModal} />\n </>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ProjectCard.js","names":[],"sources":["../../src/components/ProjectCard.tsx"],"sourcesContent":["import type React from 'react';\nimport { FolderKanban, Archive, MoreVertical, Fingerprint } from 'lucide-react';\nimport { useNavigate } from 'react-router-dom';\nimport {\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Badge,\n Button,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n EntityIdModal,\n useEntityIdModal,\n} from '@burdenoff/fe-libs/ui';\nimport { TagBadge } from '@burdenoff/fe-libs/tag';\n\nimport type { Project, ProjectTag } from '../types';\nimport { formatDate } from '../utils/formatters';\nimport { getProjectDisplayTags } from '../utils/projectTags';\nimport { getProjectStatusClasses } from '../utils/tokenHelpers';\nimport {\n useArchiveProject,\n useUnarchiveProject,\n useDeleteProject,\n} from '../hooks/useProjectMutations';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport { useScopedNavigation } from '../hooks/useScopedNavigation';\nimport { nativeConfirm, nativeImpact } from '../utils/nativeBridge';\n\ninterface ProjectCardProps {\n project: Project;\n selected?: boolean;\n onToggleSelect?: (checked: boolean) => void;\n onTagClick?: (tag: ProjectTag) => void;\n canArchive?: boolean;\n canDelete?: boolean;\n}\n\nexport function ProjectCard({\n project,\n selected = false,\n onToggleSelect,\n onTagClick,\n canArchive = false,\n canDelete = false,\n}: ProjectCardProps) {\n const navigate = useNavigate();\n const { basePath = '/workspaces' } = useWorkspacesContext();\n const { withCurrentSearch } = useScopedNavigation();\n const { mutate: archiveProject } = useArchiveProject(project.workspaceId);\n const { mutate: unarchiveProject } = useUnarchiveProject(project.workspaceId);\n const { mutate: deleteProject } = useDeleteProject(project.workspaceId);\n const idModal = useEntityIdModal();\n const projectTags = getProjectDisplayTags(project);\n\n const handleCardClick = () => {\n navigate(withCurrentSearch(`${basePath}/${project.workspaceId}/projects/${project.id}`));\n };\n\n const handleArchive = (e: React.MouseEvent) => {\n e.stopPropagation();\n if (project.archived) {\n unarchiveProject(project.id);\n } else {\n archiveProject(project.id);\n }\n };\n\n const handleDelete = async (e: React.MouseEvent) => {\n e.stopPropagation();\n void nativeImpact('medium');\n const native = await nativeConfirm({\n title: 'Delete project',\n message: `Are you sure you want to delete \"${project.name}\"?`,\n okButtonTitle: 'Delete',\n cancelButtonTitle: 'Cancel',\n });\n const ok =\n native === null ? confirm(`Are you sure you want to delete \"${project.name}\"?`) : native;\n if (ok) {\n deleteProject(project.id);\n }\n };\n\n const handleCardKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n handleCardClick();\n }\n };\n\n return (\n <>\n <Card\n className={`cursor-pointer relative ${\n selected ? 'ring-2 ring-primary/25 border-primary/50' : ''\n }`}\n onClick={handleCardClick}\n onKeyDown={handleCardKeyDown}\n role=\"button\"\n tabIndex={0}\n aria-label={`Open project ${project.name}`}\n >\n <CardHeader>\n <div className=\"flex items-start justify-between\">\n <div className=\"flex items-center gap-3 flex-1\">\n {onToggleSelect && (\n <input\n type=\"checkbox\"\n checked={selected}\n onChange={(event) => onToggleSelect(event.target.checked)}\n onClick={(event) => event.stopPropagation()}\n aria-label={`Select project ${project.name}`}\n className=\"mt-1 size-4 rounded border-border-subtle text-primary\"\n />\n )}\n <div className=\"p-3 rounded-lg bg-action-primary-bg/10 transition-colors\">\n <FolderKanban className=\"text-primary\" size={24} />\n </div>\n <div className=\"flex-1 min-w-0\">\n <CardTitle className=\"truncate\">{project.name}</CardTitle>\n <p className=\"truncate text-sm text-text-secondary\">{project.key}</p>\n </div>\n </div>\n <DropdownMenu>\n <DropdownMenuTrigger asChild onClick={(e) => e.stopPropagation()}>\n <Button variant=\"ghost\" size=\"sm\" className=\"size-8 p-0\">\n <MoreVertical size={16} />\n <span className=\"sr-only\">Open project actions</span>\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem\n onClick={(e) => {\n e.stopPropagation();\n idModal.show({\n id: project.id,\n name: project.name,\n type: 'Project',\n details: [\n { label: 'Key', value: project.key },\n { label: 'Workspace ID', value: project.workspaceId },\n ],\n });\n }}\n >\n <Fingerprint size={16} className=\"mr-2\" />\n Get ID\n </DropdownMenuItem>\n {canArchive && (\n <DropdownMenuItem onClick={handleArchive}>\n <Archive size={16} className=\"mr-2\" />\n {project.archived ? 'Unarchive' : 'Archive'}\n </DropdownMenuItem>\n )}\n {canDelete && (\n <DropdownMenuItem onClick={handleDelete} className=\"text-status-error-text\">\n Delete\n </DropdownMenuItem>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <div className=\"flex items-center gap-2\">\n <Badge className={`border ${getProjectStatusClasses(project.status)}`}>\n {project.status}\n </Badge>\n {project.archived && (\n <Badge\n variant=\"outline\"\n className=\"border-workspace-archived-border text-workspace-archived-text\"\n >\n <Archive size={12} className=\"mr-1\" />\n Archived\n </Badge>\n )}\n </div>\n <div className=\"text-xs text-text-secondary\">Created {formatDate(project.createdAt)}</div>\n {projectTags.length > 0 && (\n <div className=\"flex flex-wrap gap-1.5\" aria-label=\"Project tags\">\n {projectTags.slice(0, 4).map((tag) => (\n <span\n key={tag.tagId}\n {...(onTagClick\n ? {\n role: 'button',\n tabIndex: 0,\n onClick: (event: React.MouseEvent<HTMLSpanElement>) => {\n event.stopPropagation();\n onTagClick(tag);\n },\n onKeyDown: (event: React.KeyboardEvent<HTMLSpanElement>) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n event.stopPropagation();\n onTagClick(tag);\n }\n },\n }\n : {})}\n >\n <TagBadge\n id={tag.tagId}\n label={tag.label}\n color={tag.color ?? undefined}\n icon={tag.icon ?? undefined}\n description={tag.description ?? undefined}\n size=\"sm\"\n className={onTagClick ? 'cursor-pointer' : undefined}\n />\n </span>\n ))}\n {projectTags.length > 4 && (\n <Badge variant=\"outline\" className=\"text-xs\">\n +{projectTags.length - 4}\n </Badge>\n )}\n </div>\n )}\n </CardContent>\n </Card>\n <EntityIdModal control={idModal} />\n </>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import { useWorkspacesContext as e } from "../providers/WorkspacesProvider.js";
2
+ import { useEffect as t, useRef as n } from "react";
3
+ import { useNavigate as r } from "react-router-dom";
4
+ //#region src/components/WorkspaceContextPathSync.tsx
5
+ var i = new Set([
6
+ "list",
7
+ "new",
8
+ "invites",
9
+ "invite",
10
+ "projects",
11
+ "members",
12
+ "activity"
13
+ ]);
14
+ function a() {
15
+ let { workspaceId: a, basePath: o = "/workspaces" } = e(), s = r(), c = n(a);
16
+ return t(() => {
17
+ let e = c.current;
18
+ if (c.current = a, e === a || !a || !e || typeof window > "u") return;
19
+ let { pathname: t, search: n } = window.location, r = o.endsWith("/") ? o.slice(0, -1) : o;
20
+ if (t !== r && !t.startsWith(`${r}/`)) return;
21
+ let l = t.slice(r.length).split("/").filter(Boolean);
22
+ if (l.length === 0 || i.has(l[0]) || l[0] === a) return;
23
+ let u = `${r}/${[a, l[1]].filter(Boolean).join("/")}${n}`;
24
+ u !== `${t}${n}` && s(u, { replace: !0 });
25
+ }, [
26
+ a,
27
+ o,
28
+ s
29
+ ]), null;
30
+ }
31
+ //#endregion
32
+ export { a as WorkspaceContextPathSync };
33
+
34
+ //# sourceMappingURL=WorkspaceContextPathSync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WorkspaceContextPathSync.js","names":[],"sources":["../../src/components/WorkspaceContextPathSync.tsx"],"sourcesContent":["import { useEffect, useRef } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\n\n// First path segments that are NOT a `:workspaceId` — the unscoped, context-driven\n// routes (see WorkspacesRoutes). When the URL starts with one of these there is no\n// workspace segment to keep in sync.\nconst UNSCOPED_FIRST_SEGMENTS = new Set([\n 'list',\n 'new',\n 'invites',\n 'invite',\n 'projects',\n 'members',\n 'activity',\n]);\n\n/**\n * Keeps the workspace-scoped URL (`/workspaces/:workspaceId/...`) in step with the\n * active context.\n *\n * The route path `:workspaceId` intentionally OVERRIDES the active context for data\n * fetching (deep-link / drill into a specific workspace). The hazard is that the\n * chrome context switcher updates the active context (+ `?workspace=` query +\n * storage) but cannot rewrite this MFE's route path — so after a switch the path\n * segment goes stale and its override silently points the page at the previously\n * viewed workspace (wrong project list, create lands in the old workspace, etc.;\n * BOFF-2805).\n *\n * Fix: when (and only when) the active context workspace CHANGES, rewrite the path's\n * workspace segment to the new context. Firing on change — not on mount — preserves\n * deep-links and drill-in (those change the path, not the context, so they are left\n * alone and their override stands). Nested resource ids (`/projects/:projectId/...`)\n * are dropped because they belong to the old workspace; we land on the page-level\n * route for the new workspace. The query string (`?workspace=…&org=…`) is preserved.\n *\n * One-directional (context → path); the path never writes back to context, so there\n * is no feedback loop.\n */\nexport function WorkspaceContextPathSync() {\n const { workspaceId: contextWorkspaceId, basePath = '/workspaces' } = useWorkspacesContext();\n const navigate = useNavigate();\n const previousContextWorkspaceIdRef = useRef(contextWorkspaceId);\n\n useEffect(() => {\n const previousContextWorkspaceId = previousContextWorkspaceIdRef.current;\n previousContextWorkspaceIdRef.current = contextWorkspaceId;\n\n // Only react to an actual context switch — never on initial mount or deep-link\n // (those must keep whatever workspace the URL already points at). The active\n // context workspace is the sole trigger, so it is the only dependency; the\n // current URL is read inside the effect (not a dependency) — it is mutable\n // browser state, not a reactive value (react-doctor/no-mutable-in-deps).\n if (previousContextWorkspaceId === contextWorkspaceId) return;\n if (!contextWorkspaceId) return;\n // Ignore the FIRST hydration (empty -> first resolved workspace). The active\n // context resolves asynchronously from URL/storage, so the initial\n // `undefined -> <stored>` transition is NOT a user-initiated switch — treating\n // it as one would rewrite a scoped deep-link (`/workspaces/<linked>/projects`\n // loaded with a different stored workspace) to the stored one. Only a real\n // workspace -> different workspace is a switch.\n if (!previousContextWorkspaceId) return;\n if (typeof window === 'undefined') return;\n\n const { pathname, search } = window.location;\n const cleanBase = basePath.endsWith('/') ? basePath.slice(0, -1) : basePath;\n if (pathname !== cleanBase && !pathname.startsWith(`${cleanBase}/`)) {\n return;\n }\n\n const segments = pathname.slice(cleanBase.length).split('/').filter(Boolean);\n\n // Unscoped / context-driven route — nothing to rewrite.\n if (segments.length === 0 || UNSCOPED_FIRST_SEGMENTS.has(segments[0]!)) return;\n\n // segments[0] is a `:workspaceId`. Already aligned → done.\n if (segments[0] === contextWorkspaceId) return;\n\n // Rewrite the workspace segment to the new context, keep the page-type segment\n // (projects / members / activity / settings / invite), drop any nested resource\n // ids that belong to the old workspace. The query string (`?workspace=…&org=…`)\n // is preserved; the host ActiveContextProvider syncs `?workspace=` to the new\n // context right after, so the URL settles to path == ?workspace == context.\n const pageTypeSegment = segments[1];\n const nextSegments = [contextWorkspaceId, pageTypeSegment].filter(Boolean);\n const nextPath = `${cleanBase}/${nextSegments.join('/')}${search}`;\n\n if (nextPath !== `${pathname}${search}`) {\n navigate(nextPath, { replace: true });\n }\n }, [contextWorkspaceId, basePath, navigate]);\n\n return null;\n}\n"],"mappings":";;;;AAOA,IAAM,IAA0B,IAAI,IAAI;CACtC;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;AAwBF,SAAgB,IAA2B;CACzC,IAAM,EAAE,aAAa,GAAoB,cAAW,kBAAkB,GAAsB,EACtF,IAAW,GAAa,EACxB,IAAgC,EAAO,EAAmB;AAkDhE,QAhDA,QAAgB;EACd,IAAM,IAA6B,EAA8B;AAiBjE,MAhBA,EAA8B,UAAU,GAOpC,MAA+B,KAC/B,CAAC,KAOD,CAAC,KACD,OAAO,SAAW,IAAa;EAEnC,IAAM,EAAE,aAAU,cAAW,OAAO,UAC9B,IAAY,EAAS,SAAS,IAAI,GAAG,EAAS,MAAM,GAAG,GAAG,GAAG;AACnE,MAAI,MAAa,KAAa,CAAC,EAAS,WAAW,GAAG,EAAU,GAAG,CACjE;EAGF,IAAM,IAAW,EAAS,MAAM,EAAU,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,QAAQ;AAM5E,MAHI,EAAS,WAAW,KAAK,EAAwB,IAAI,EAAS,GAAI,IAGlE,EAAS,OAAO,EAAoB;EASxC,IAAM,IAAW,GAAG,EAAU,GADT,CAAC,GADE,EAAS,GACyB,CAAC,OAAO,QAAQ,CAC5B,KAAK,IAAI,GAAG;AAE1D,EAAI,MAAa,GAAG,IAAW,OAC7B,EAAS,GAAU,EAAE,SAAS,IAAM,CAAC;IAEtC;EAAC;EAAoB;EAAU;EAAS,CAAC,EAErC"}
@@ -1 +1 @@
1
- {"version":3,"file":"ActivityListPage.js","names":[],"sources":["../../src/pages/ActivityListPage.tsx"],"sourcesContent":["import { Activity } from 'lucide-react';\nimport { useParams } from 'react-router-dom';\nimport { Card, CardContent, CardHeader, CardTitle, Skeleton } from '@burdenoff/fe-libs/ui';\nimport { useWorkspaceActivity } from '../hooks/useWorkspaceActivity';\nimport { useWorkspacePermissions } from '../hooks/useWorkspacePermissions';\nimport { useWorkspacesPageView } from '../hooks/useWorkspacesPageView';\nimport { formatDate } from '../utils/formatters';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../utils/i18n';\n\nexport function ActivityListPage() {\n const { t } = useI18n();\n\n const { workspaceId } = useParams<{ workspaceId: string }>();\n useWorkspacesPageView('workspaces.activity', { workspaceId, requireParams: true });\n const { data: activities, isLoading, error } = useWorkspaceActivity(workspaceId);\n const { canViewActivity, isLoading: permissionsLoading } = useWorkspacePermissions();\n\n if (permissionsLoading) {\n return (\n <div className=\"space-y-4 p-6\">\n <Skeleton className=\"h-10 w-1/3\" />\n <Skeleton className=\"h-64\" />\n </div>\n );\n }\n\n if (!canViewActivity) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold\">\n {tWithFallback(t, 'activityList.accessDenied', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n 'activityList.accessDeniedDescription',\n \"You don't have permission to view activity.\"\n )}\n </p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n <div>\n <h1 className=\"text-2xl sm:text-3xl font-bold\">\n {tWithFallback(t, 'activityList.title', 'Activity')}\n </h1>\n <p className=\"text-text-secondary\">\n {tWithFallback(t, 'activityList.description', 'Track workspace activity')}\n </p>\n </div>\n\n {isLoading ? (\n <Card>\n <CardContent className=\"py-8 text-sm text-text-secondary\">\n {tWithFallback(t, 'activityList.loading', 'Loading activity…')}\n </CardContent>\n </Card>\n ) : error ? (\n <Card>\n <CardContent className=\"py-8 text-sm text-status-error-text\">\n {error instanceof Error\n ? error.message\n : tWithFallback(t, 'activityList.loadError', 'Failed to load activity')}\n </CardContent>\n </Card>\n ) : (activities?.length ?? 0) === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <div className=\"rounded-full bg-bg-sunken p-6 mb-4\">\n <Activity size={48} className=\"text-text-secondary\" />\n </div>\n <h3 className=\"text-lg font-semibold mb-2\">\n {tWithFallback(t, 'activityList.emptyTitle', 'No recent activity')}\n </h3>\n <p className=\"text-text-secondary max-w-sm\">\n {tWithFallback(\n t,\n 'activityList.emptyDescription',\n 'Recent workspace actions will appear here as your team creates, updates, and manages work.'\n )}\n </p>\n </div>\n ) : (\n <div className=\"space-y-3\">\n {activities?.map((item) => (\n <Card key={item.id}>\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-base\">{item.description || item.action}</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-1 text-sm text-text-secondary\">\n <p>\n {tWithFallback(t, 'activityList.labelAction', 'Action')}:{' '}\n <span className=\"text-text-primary\">{item.action}</span>\n </p>\n {item.resourceType && (\n <p>\n {tWithFallback(t, 'activityList.labelResource', 'Resource')}:{' '}\n <span className=\"text-text-primary\">{item.resourceType}</span>\n </p>\n )}\n <p>\n {tWithFallback(t, 'activityList.labelWhen', 'When')}:{' '}\n <span className=\"text-text-primary\">{formatDate(item.timestamp)}</span>\n </p>\n <p>\n {tWithFallback(t, 'activityList.labelActor', 'Actor')}:{' '}\n <span className=\"break-all text-text-primary\">{item.userId}</span>\n </p>\n </CardContent>\n </Card>\n ))}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ActivityListPage.js","names":[],"sources":["../../src/pages/ActivityListPage.tsx"],"sourcesContent":["import { Activity } from 'lucide-react';\nimport { useParams } from 'react-router-dom';\nimport { Card, CardContent, CardHeader, CardTitle, Skeleton } from '@burdenoff/fe-libs/ui';\nimport { useWorkspaceActivity } from '../hooks/useWorkspaceActivity';\nimport { useWorkspacePermissions } from '../hooks/useWorkspacePermissions';\nimport { useWorkspacesPageView } from '../hooks/useWorkspacesPageView';\nimport { formatDate } from '../utils/formatters';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../utils/i18n';\n\nexport function ActivityListPage() {\n const { t } = useI18n();\n\n // The URL path `:workspaceId` (when present) intentionally overrides the active\n // context. WorkspaceContextPathSync rewrites this segment on a context switch so\n // the override can't point at a stale workspace. (BOFF-2805 follow-up)\n const { workspaceId } = useParams<{ workspaceId: string }>();\n useWorkspacesPageView('workspaces.activity', { workspaceId, requireParams: true });\n const { data: activities, isLoading, error } = useWorkspaceActivity(workspaceId);\n const { canViewActivity, isLoading: permissionsLoading } = useWorkspacePermissions();\n\n if (permissionsLoading) {\n return (\n <div className=\"space-y-4 p-6\">\n <Skeleton className=\"h-10 w-1/3\" />\n <Skeleton className=\"h-64\" />\n </div>\n );\n }\n\n if (!canViewActivity) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold\">\n {tWithFallback(t, 'activityList.accessDenied', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n 'activityList.accessDeniedDescription',\n \"You don't have permission to view activity.\"\n )}\n </p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n <div>\n <h1 className=\"text-2xl sm:text-3xl font-bold\">\n {tWithFallback(t, 'activityList.title', 'Activity')}\n </h1>\n <p className=\"text-text-secondary\">\n {tWithFallback(t, 'activityList.description', 'Track workspace activity')}\n </p>\n </div>\n\n {isLoading ? (\n <Card>\n <CardContent className=\"py-8 text-sm text-text-secondary\">\n {tWithFallback(t, 'activityList.loading', 'Loading activity…')}\n </CardContent>\n </Card>\n ) : error ? (\n <Card>\n <CardContent className=\"py-8 text-sm text-status-error-text\">\n {error instanceof Error\n ? error.message\n : tWithFallback(t, 'activityList.loadError', 'Failed to load activity')}\n </CardContent>\n </Card>\n ) : (activities?.length ?? 0) === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <div className=\"rounded-full bg-bg-sunken p-6 mb-4\">\n <Activity size={48} className=\"text-text-secondary\" />\n </div>\n <h3 className=\"text-lg font-semibold mb-2\">\n {tWithFallback(t, 'activityList.emptyTitle', 'No recent activity')}\n </h3>\n <p className=\"text-text-secondary max-w-sm\">\n {tWithFallback(\n t,\n 'activityList.emptyDescription',\n 'Recent workspace actions will appear here as your team creates, updates, and manages work.'\n )}\n </p>\n </div>\n ) : (\n <div className=\"space-y-3\">\n {activities?.map((item) => (\n <Card key={item.id}>\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-base\">{item.description || item.action}</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-1 text-sm text-text-secondary\">\n <p>\n {tWithFallback(t, 'activityList.labelAction', 'Action')}:{' '}\n <span className=\"text-text-primary\">{item.action}</span>\n </p>\n {item.resourceType && (\n <p>\n {tWithFallback(t, 'activityList.labelResource', 'Resource')}:{' '}\n <span className=\"text-text-primary\">{item.resourceType}</span>\n </p>\n )}\n <p>\n {tWithFallback(t, 'activityList.labelWhen', 'When')}:{' '}\n <span className=\"text-text-primary\">{formatDate(item.timestamp)}</span>\n </p>\n <p>\n {tWithFallback(t, 'activityList.labelActor', 'Actor')}:{' '}\n <span className=\"break-all text-text-primary\">{item.userId}</span>\n </p>\n </CardContent>\n </Card>\n ))}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}