@burdenoff/microfe-workspaces 2026.830.1 → 2026.915.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/CreateProjectDialog.js +65 -61
- package/dist/components/CreateProjectDialog.js.map +1 -1
- package/dist/components/CreateWorkspaceDialog.js +93 -90
- package/dist/components/CreateWorkspaceDialog.js.map +1 -1
- package/dist/components/InviteMemberDialog.js +122 -111
- package/dist/components/InviteMemberDialog.js.map +1 -1
- package/dist/components/MemberListItem.js +55 -58
- package/dist/components/MemberListItem.js.map +1 -1
- package/dist/components/ProjectCard.js +93 -92
- package/dist/components/ProjectCard.js.map +1 -1
- package/dist/components/ProjectMemberPickerDialog.js +78 -93
- package/dist/components/ProjectMemberPickerDialog.js.map +1 -1
- package/dist/components/RenameProjectDialog.js +47 -44
- package/dist/components/RenameProjectDialog.js.map +1 -1
- package/dist/components/WorkspaceCard.js +42 -43
- package/dist/components/WorkspaceCard.js.map +1 -1
- package/dist/components/WorkspacesErrorBoundary.js +43 -34
- package/dist/components/WorkspacesErrorBoundary.js.map +1 -1
- package/dist/components/tags/ProjectTagsWidget.js +75 -77
- package/dist/components/tags/ProjectTagsWidget.js.map +1 -1
- package/dist/hooks/useTr.js +14 -0
- package/dist/hooks/useTr.js.map +1 -0
- package/dist/index.js +2 -1
- package/dist/pages/ActivityListPage.js +3 -3
- package/dist/pages/CreateWorkspacePage.js +10 -10
- package/dist/pages/InviteAcceptancePage.js +49 -49
- package/dist/pages/InviteAcceptancePage.js.map +1 -1
- package/dist/pages/MembersListPage.js +6 -6
- package/dist/pages/PendingInvitesPage.js +3 -3
- package/dist/pages/ProjectDetailPage.js +4 -4
- package/dist/pages/ProjectDetailPage.js.map +1 -1
- package/dist/pages/ProjectMembersPage.js +3 -3
- package/dist/pages/ProjectsListPage.js +5 -5
- package/dist/pages/WorkspaceDetailPage.js +4 -4
- package/dist/pages/WorkspaceInvitePage.js +4 -4
- package/dist/pages/WorkspaceSettingsPage.js +7 -7
- package/dist/pages/WorkspacesListPage.js +9 -9
- package/dist/pages/dashboard/widgets/RecentActivityWidget.js +4 -4
- package/dist/pages/dashboard/widgets/StorageWidget.js +3 -3
- package/dist/pages/dashboard/widgets/WelcomeWidget.js +26 -16
- package/dist/pages/dashboard/widgets/WelcomeWidget.js.map +1 -1
- package/dist/translations/en.js +908 -0
- package/dist/translations/en.js.map +1 -0
- package/package.json +1 -1
|
@@ -1,79 +1,78 @@
|
|
|
1
1
|
import { useWorkspacesContext as e } from "../providers/WorkspacesProvider.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
2
|
+
import { useTr as t } from "../hooks/useTr.js";
|
|
3
|
+
import { getWorkspaceStatusClasses as n } from "../utils/tokenHelpers.js";
|
|
4
|
+
import { useScopedNavigation as r } from "../hooks/useScopedNavigation.js";
|
|
5
|
+
import { Building2 as i, FolderKanban as a, Users as o } from "lucide-react";
|
|
6
|
+
import { useActiveContext as s } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
7
|
+
import { jsx as c, jsxs as l } from "react/jsx-runtime";
|
|
8
|
+
import { Badge as u, CardContent as d, CardHeader as f, CardTitle as p, GlassCard as m } from "@burdenoff/fe-libs/ui";
|
|
9
|
+
import { useNavigate as h } from "react-router";
|
|
9
10
|
//#region src/components/WorkspaceCard.tsx
|
|
10
|
-
function
|
|
11
|
-
let
|
|
12
|
-
let e =
|
|
13
|
-
e &&
|
|
11
|
+
function g({ workspace: g }) {
|
|
12
|
+
let _ = t(), v = h(), { basePath: y = "/" } = e(), { withCurrentSearch: b } = r(), { setOrganization: x, setWorkspace: S, setProject: C, setTenant: w } = s(), T = () => {
|
|
13
|
+
let e = g.organizationId ?? g.orgId;
|
|
14
|
+
e && x(e), S(g.id), C(null), typeof g.tenantId == "string" && w(g.tenantId), v(b(`${y}/${g.id}`));
|
|
14
15
|
};
|
|
15
|
-
return /* @__PURE__ */
|
|
16
|
+
return /* @__PURE__ */ l(m, {
|
|
16
17
|
treatment: "insight",
|
|
17
18
|
className: "cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-action-primary-bg/50",
|
|
18
|
-
onClick:
|
|
19
|
+
onClick: T,
|
|
19
20
|
onKeyDown: (e) => {
|
|
20
|
-
(e.key === "Enter" || e.key === " ") && (e.preventDefault(),
|
|
21
|
+
(e.key === "Enter" || e.key === " ") && (e.preventDefault(), T());
|
|
21
22
|
},
|
|
22
23
|
role: "button",
|
|
23
24
|
tabIndex: 0,
|
|
24
|
-
"aria-label":
|
|
25
|
+
"aria-label": _("workspaceCard.openWorkspaceAriaLabel", "Open workspace {{name}}", { name: g.name }),
|
|
25
26
|
"data-tour": "workspaces-card",
|
|
26
|
-
children: [/* @__PURE__ */
|
|
27
|
+
children: [/* @__PURE__ */ c(f, { children: /* @__PURE__ */ l("div", {
|
|
27
28
|
className: "flex items-center gap-3",
|
|
28
|
-
children: [/* @__PURE__ */
|
|
29
|
+
children: [/* @__PURE__ */ c("div", {
|
|
29
30
|
className: "p-3 rounded-lg bg-action-primary-bg/10 transition-colors",
|
|
30
|
-
children: /* @__PURE__ */
|
|
31
|
+
children: /* @__PURE__ */ c(i, {
|
|
31
32
|
className: "text-primary",
|
|
32
33
|
size: 24
|
|
33
34
|
})
|
|
34
|
-
}), /* @__PURE__ */
|
|
35
|
+
}), /* @__PURE__ */ l("div", {
|
|
35
36
|
className: "flex-1 min-w-0",
|
|
36
|
-
children: [/* @__PURE__ */
|
|
37
|
+
children: [/* @__PURE__ */ c(p, {
|
|
37
38
|
className: "text-lg truncate",
|
|
38
|
-
children:
|
|
39
|
-
}), /* @__PURE__ */
|
|
39
|
+
children: g.name
|
|
40
|
+
}), /* @__PURE__ */ c("div", {
|
|
40
41
|
className: "flex items-center gap-2 mt-1",
|
|
41
|
-
children: /* @__PURE__ */
|
|
42
|
-
className: `text-xs border ${
|
|
43
|
-
children:
|
|
42
|
+
children: /* @__PURE__ */ c(u, {
|
|
43
|
+
className: `text-xs border ${n(g.status)}`,
|
|
44
|
+
children: g.status
|
|
44
45
|
})
|
|
45
46
|
})]
|
|
46
47
|
})]
|
|
47
|
-
}) }), /* @__PURE__ */
|
|
48
|
+
}) }), /* @__PURE__ */ l(d, { children: [/* @__PURE__ */ l("div", {
|
|
48
49
|
className: "flex flex-wrap gap-4 text-sm text-text-secondary",
|
|
49
|
-
children: [typeof
|
|
50
|
+
children: [typeof g.memberCount == "number" && g.memberCount > 0 && /* @__PURE__ */ l("div", {
|
|
50
51
|
className: "flex items-center gap-1",
|
|
51
|
-
children: [/* @__PURE__ */
|
|
52
|
+
children: [/* @__PURE__ */ c(o, {
|
|
52
53
|
size: 16,
|
|
53
54
|
"aria-hidden": "true"
|
|
54
|
-
}), /* @__PURE__ */ c("span", { children:
|
|
55
|
-
|
|
56
|
-
"
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}), typeof h.projectCount == "number" && h.projectCount > 0 && /* @__PURE__ */ c("div", {
|
|
55
|
+
}), /* @__PURE__ */ c("span", { children: _("workspaceCard.memberCount", "{{count}} member{{suffix}}", {
|
|
56
|
+
count: g.memberCount,
|
|
57
|
+
suffix: g.memberCount === 1 ? "" : "s"
|
|
58
|
+
}) })]
|
|
59
|
+
}), typeof g.projectCount == "number" && g.projectCount > 0 && /* @__PURE__ */ l("div", {
|
|
60
60
|
className: "flex items-center gap-1",
|
|
61
|
-
children: [/* @__PURE__ */
|
|
61
|
+
children: [/* @__PURE__ */ c(a, {
|
|
62
62
|
size: 16,
|
|
63
63
|
"aria-hidden": "true"
|
|
64
|
-
}), /* @__PURE__ */ c("span", { children:
|
|
65
|
-
|
|
66
|
-
"
|
|
67
|
-
|
|
68
|
-
] })]
|
|
64
|
+
}), /* @__PURE__ */ c("span", { children: _("workspaceCard.projectCount", "{{count}} project{{suffix}}", {
|
|
65
|
+
count: g.projectCount,
|
|
66
|
+
suffix: g.projectCount === 1 ? "" : "s"
|
|
67
|
+
}) })]
|
|
69
68
|
})]
|
|
70
|
-
}),
|
|
69
|
+
}), g.domain && /* @__PURE__ */ c("p", {
|
|
71
70
|
className: "mt-2 truncate text-xs text-text-muted",
|
|
72
|
-
children:
|
|
71
|
+
children: g.domain
|
|
73
72
|
})] })]
|
|
74
73
|
});
|
|
75
74
|
}
|
|
76
75
|
//#endregion
|
|
77
|
-
export {
|
|
76
|
+
export { g as WorkspaceCard };
|
|
78
77
|
|
|
79
78
|
//# sourceMappingURL=WorkspaceCard.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkspaceCard.js","names":[],"sources":["../../src/components/WorkspaceCard.tsx"],"sourcesContent":["import { Building2, Users, FolderKanban } from 'lucide-react';\nimport { useNavigate } from 'react-router';\nimport { GlassCard, CardHeader, CardTitle, CardContent, Badge } from '@burdenoff/fe-libs/ui';\nimport { useActiveContext } from '@burdenoff/fe-libs/shared/providers/shell';\nimport type { Workspace } from '../types';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport { getWorkspaceStatusClasses } from '../utils/tokenHelpers';\nimport { useScopedNavigation } from '../hooks/useScopedNavigation';\n\ninterface WorkspaceCardProps {\n workspace: Workspace;\n}\n\nexport function WorkspaceCard({ workspace }: WorkspaceCardProps) {\n const navigate = useNavigate();\n const { basePath = '/' } = useWorkspacesContext();\n const { withCurrentSearch } = useScopedNavigation();\n const { setOrganization, setWorkspace, setProject, setTenant } = useActiveContext();\n\n const openWorkspace = () => {\n // Drilling into a workspace COMMITS it as the active selection (BOFF-2821 G1):\n // the workspace token + `x-workspace-id` follow the committed selection, so\n // without this the detail page at `/workspaces/<this>` would render with the\n // *previously selected* workspace's token and 500. Setting the selection also\n // re-issues the token (via the header's context-sync effects) and syncs the\n // `?workspace=…` query; the path is the authority for the page itself.\n // The list is populated from the global `myWorkspaces` query, whose items\n // carry `organizationId`; the local Workspace type still names the field\n // `orgId`. Read both so the org commits regardless of the source query.\n const organizationId =\n (workspace as { organizationId?: string }).organizationId ?? workspace.orgId;\n if (organizationId) {\n setOrganization(organizationId);\n }\n setWorkspace(workspace.id);\n setProject(null);\n if (typeof workspace.tenantId === 'string') {\n setTenant(workspace.tenantId);\n }\n navigate(withCurrentSearch(`${basePath}/${workspace.id}`));\n };\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n openWorkspace();\n }\n };\n\n return (\n <GlassCard\n treatment=\"insight\"\n className=\"cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-action-primary-bg/50\"\n onClick={openWorkspace}\n onKeyDown={handleKeyDown}\n role=\"button\"\n tabIndex={0}\n aria-label={
|
|
1
|
+
{"version":3,"file":"WorkspaceCard.js","names":[],"sources":["../../src/components/WorkspaceCard.tsx"],"sourcesContent":["import { Building2, Users, FolderKanban } from 'lucide-react';\nimport { useNavigate } from 'react-router';\nimport { GlassCard, CardHeader, CardTitle, CardContent, Badge } from '@burdenoff/fe-libs/ui';\nimport { useActiveContext } from '@burdenoff/fe-libs/shared/providers/shell';\nimport type { Workspace } from '../types';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport { getWorkspaceStatusClasses } from '../utils/tokenHelpers';\nimport { useScopedNavigation } from '../hooks/useScopedNavigation';\nimport { useTr } from '../hooks/useTr';\n\ninterface WorkspaceCardProps {\n workspace: Workspace;\n}\n\nexport function WorkspaceCard({ workspace }: WorkspaceCardProps) {\n const tr = useTr();\n const navigate = useNavigate();\n const { basePath = '/' } = useWorkspacesContext();\n const { withCurrentSearch } = useScopedNavigation();\n const { setOrganization, setWorkspace, setProject, setTenant } = useActiveContext();\n\n const openWorkspace = () => {\n // Drilling into a workspace COMMITS it as the active selection (BOFF-2821 G1):\n // the workspace token + `x-workspace-id` follow the committed selection, so\n // without this the detail page at `/workspaces/<this>` would render with the\n // *previously selected* workspace's token and 500. Setting the selection also\n // re-issues the token (via the header's context-sync effects) and syncs the\n // `?workspace=…` query; the path is the authority for the page itself.\n // The list is populated from the global `myWorkspaces` query, whose items\n // carry `organizationId`; the local Workspace type still names the field\n // `orgId`. Read both so the org commits regardless of the source query.\n const organizationId =\n (workspace as { organizationId?: string }).organizationId ?? workspace.orgId;\n if (organizationId) {\n setOrganization(organizationId);\n }\n setWorkspace(workspace.id);\n setProject(null);\n if (typeof workspace.tenantId === 'string') {\n setTenant(workspace.tenantId);\n }\n navigate(withCurrentSearch(`${basePath}/${workspace.id}`));\n };\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n openWorkspace();\n }\n };\n\n return (\n <GlassCard\n treatment=\"insight\"\n className=\"cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-action-primary-bg/50\"\n onClick={openWorkspace}\n onKeyDown={handleKeyDown}\n role=\"button\"\n tabIndex={0}\n aria-label={tr('workspaceCard.openWorkspaceAriaLabel', 'Open workspace {{name}}', {\n name: workspace.name,\n })}\n data-tour=\"workspaces-card\"\n >\n <CardHeader>\n <div className=\"flex items-center gap-3\">\n <div className=\"p-3 rounded-lg bg-action-primary-bg/10 transition-colors\">\n <Building2 className=\"text-primary\" size={24} />\n </div>\n <div className=\"flex-1 min-w-0\">\n <CardTitle className=\"text-lg truncate\">{workspace.name}</CardTitle>\n <div className=\"flex items-center gap-2 mt-1\">\n <Badge className={`text-xs border ${getWorkspaceStatusClasses(workspace.status)}`}>\n {workspace.status}\n </Badge>\n </div>\n </div>\n </div>\n </CardHeader>\n <CardContent>\n <div className=\"flex flex-wrap gap-4 text-sm text-text-secondary\">\n {typeof workspace.memberCount === 'number' && workspace.memberCount > 0 && (\n <div className=\"flex items-center gap-1\">\n <Users size={16} aria-hidden=\"true\" />\n <span>\n {tr('workspaceCard.memberCount', '{{count}} member{{suffix}}', {\n count: workspace.memberCount,\n suffix: workspace.memberCount === 1 ? '' : 's',\n })}\n </span>\n </div>\n )}\n {typeof workspace.projectCount === 'number' && workspace.projectCount > 0 && (\n <div className=\"flex items-center gap-1\">\n <FolderKanban size={16} aria-hidden=\"true\" />\n <span>\n {tr('workspaceCard.projectCount', '{{count}} project{{suffix}}', {\n count: workspace.projectCount,\n suffix: workspace.projectCount === 1 ? '' : 's',\n })}\n </span>\n </div>\n )}\n </div>\n {workspace.domain && (\n <p className=\"mt-2 truncate text-xs text-text-muted\">{workspace.domain}</p>\n )}\n </CardContent>\n </GlassCard>\n );\n}\n"],"mappings":";;;;;;;;;;AAcA,SAAgB,EAAc,EAAE,gBAAiC;CAC/D,IAAM,IAAK,GAAO,EACZ,IAAW,GAAa,EACxB,EAAE,cAAW,QAAQ,GAAsB,EAC3C,EAAE,yBAAsB,GAAqB,EAC7C,EAAE,oBAAiB,iBAAc,eAAY,iBAAc,GAAkB,EAE7E,UAAsB;EAU1B,IAAM,IACH,EAA0C,kBAAkB,EAAU;AASzE,EARI,KACF,EAAgB,EAAe,EAEjC,EAAa,EAAU,GAAG,EAC1B,EAAW,KAAK,EACZ,OAAO,EAAU,YAAa,YAChC,EAAU,EAAU,SAAS,EAE/B,EAAS,EAAkB,GAAG,EAAS,GAAG,EAAU,KAAK,CAAC;;AAU5D,QACE,kBAAC,GAAD;EACE,WAAU;EACV,WAAU;EACV,SAAS;EACT,YAZmB,MAA+C;AACpE,IAAI,EAAM,QAAQ,WAAW,EAAM,QAAQ,SACzC,EAAM,gBAAgB,EACtB,GAAe;;EAUf,MAAK;EACL,UAAU;EACV,cAAY,EAAG,wCAAwC,2BAA2B,EAChF,MAAM,EAAU,MACjB,CAAC;EACF,aAAU;YAVZ,CAYE,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KAAW,WAAU;KAAe,MAAM;KAAM,CAAA;IAC5C,CAAA,EACN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAW,WAAU;eAAoB,EAAU;KAAiB,CAAA,EACpE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD;MAAO,WAAW,kBAAkB,EAA0B,EAAU,OAAO;gBAC5E,EAAU;MACL,CAAA;KACJ,CAAA,CACF;MACF;MACK,CAAA,EACb,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACG,OAAO,EAAU,eAAgB,YAAY,EAAU,cAAc,KACpE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAO,MAAM;KAAI,eAAY;KAAS,CAAA,EACtC,kBAAC,QAAD,EAAA,UACG,EAAG,6BAA6B,8BAA8B;KAC7D,OAAO,EAAU;KACjB,QAAQ,EAAU,gBAAgB,IAAI,KAAK;KAC5C,CAAC,EACG,CAAA,CACH;OAEP,OAAO,EAAU,gBAAiB,YAAY,EAAU,eAAe,KACtE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAc,MAAM;KAAI,eAAY;KAAS,CAAA,EAC7C,kBAAC,QAAD,EAAA,UACG,EAAG,8BAA8B,+BAA+B;KAC/D,OAAO,EAAU;KACjB,QAAQ,EAAU,iBAAiB,IAAI,KAAK;KAC7C,CAAC,EACG,CAAA,CACH;MAEJ;MACL,EAAU,UACT,kBAAC,KAAD;GAAG,WAAU;aAAyC,EAAU;GAAW,CAAA,CAEjE,EAAA,CAAA,CACJ"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { useTr as e } from "../hooks/useTr.js";
|
|
2
|
+
import { Component as t } from "react";
|
|
3
|
+
import { AlertCircle as n, Home as r, RefreshCw as i } from "lucide-react";
|
|
4
|
+
import { jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
5
|
+
import { Button as s } from "@burdenoff/fe-libs/ui";
|
|
5
6
|
//#region src/components/WorkspacesErrorBoundary.tsx
|
|
6
|
-
var
|
|
7
|
+
var c = class extends t {
|
|
7
8
|
constructor(e) {
|
|
8
9
|
super(e), this.state = {
|
|
9
10
|
hasError: !1,
|
|
@@ -56,80 +57,81 @@ var s = class extends e {
|
|
|
56
57
|
window.location.href = "/";
|
|
57
58
|
};
|
|
58
59
|
render() {
|
|
59
|
-
|
|
60
|
+
let { tr: e } = this.props;
|
|
61
|
+
return this.state.hasError ? this.props.fallback ? this.props.fallback : /* @__PURE__ */ a("div", {
|
|
60
62
|
className: "flex items-center justify-center min-h-[400px] p-6",
|
|
61
|
-
children: /* @__PURE__ */
|
|
63
|
+
children: /* @__PURE__ */ o("div", {
|
|
62
64
|
className: "max-w-md w-full text-center space-y-6",
|
|
63
65
|
children: [
|
|
64
|
-
/* @__PURE__ */
|
|
66
|
+
/* @__PURE__ */ a("div", {
|
|
65
67
|
className: "flex justify-center",
|
|
66
|
-
children: /* @__PURE__ */
|
|
68
|
+
children: /* @__PURE__ */ a("div", {
|
|
67
69
|
className: "rounded-full bg-status-error-bg/10 p-6",
|
|
68
|
-
children: /* @__PURE__ */
|
|
70
|
+
children: /* @__PURE__ */ a(n, { className: "size-12 text-status-error-text" })
|
|
69
71
|
})
|
|
70
72
|
}),
|
|
71
|
-
/* @__PURE__ */
|
|
73
|
+
/* @__PURE__ */ o("div", {
|
|
72
74
|
className: "space-y-2",
|
|
73
|
-
children: [/* @__PURE__ */
|
|
75
|
+
children: [/* @__PURE__ */ a("h2", {
|
|
74
76
|
className: "text-2xl font-bold",
|
|
75
|
-
children: "Something went wrong"
|
|
76
|
-
}), /* @__PURE__ */
|
|
77
|
+
children: e("errorBoundary.title", "Something went wrong")
|
|
78
|
+
}), /* @__PURE__ */ a("p", {
|
|
77
79
|
className: "text-text-secondary",
|
|
78
|
-
children: "We encountered an unexpected error. Please try refreshing the page or contact support if the problem persists."
|
|
80
|
+
children: e("errorBoundary.description", "We encountered an unexpected error. Please try refreshing the page or contact support if the problem persists.")
|
|
79
81
|
})]
|
|
80
82
|
}),
|
|
81
|
-
process.env.NODE_ENV === "development" && this.state.error && /* @__PURE__ */
|
|
83
|
+
process.env.NODE_ENV === "development" && this.state.error && /* @__PURE__ */ o("details", {
|
|
82
84
|
className: "text-left",
|
|
83
|
-
children: [/* @__PURE__ */
|
|
85
|
+
children: [/* @__PURE__ */ a("summary", {
|
|
84
86
|
className: "cursor-pointer text-sm font-medium text-text-secondary hover:text-text-primary transition-colors duration-200",
|
|
85
|
-
children: "Error Details"
|
|
86
|
-
}), /* @__PURE__ */
|
|
87
|
+
children: e("errorBoundary.errorDetails", "Error Details")
|
|
88
|
+
}), /* @__PURE__ */ o("div", {
|
|
87
89
|
className: "mt-2 p-4 bg-bg-sunken rounded-lg text-xs font-mono overflow-auto max-h-40",
|
|
88
90
|
children: [
|
|
89
|
-
/* @__PURE__ */
|
|
91
|
+
/* @__PURE__ */ o("p", {
|
|
90
92
|
className: "font-semibold text-status-error-text",
|
|
91
93
|
children: [this.state.error.name, ":"]
|
|
92
94
|
}),
|
|
93
|
-
/* @__PURE__ */
|
|
95
|
+
/* @__PURE__ */ a("p", {
|
|
94
96
|
className: "text-text-primary",
|
|
95
97
|
children: this.state.error.message
|
|
96
98
|
}),
|
|
97
|
-
this.state.error.stack && /* @__PURE__ */
|
|
99
|
+
this.state.error.stack && /* @__PURE__ */ a("pre", {
|
|
98
100
|
className: "mt-2 text-text-secondary text-xs overflow-x-auto",
|
|
99
101
|
children: this.state.error.stack
|
|
100
102
|
})
|
|
101
103
|
]
|
|
102
104
|
})]
|
|
103
105
|
}),
|
|
104
|
-
/* @__PURE__ */
|
|
106
|
+
/* @__PURE__ */ o("div", {
|
|
105
107
|
className: "flex flex-col sm:flex-row gap-3 justify-center",
|
|
106
108
|
children: [
|
|
107
|
-
/* @__PURE__ */
|
|
109
|
+
/* @__PURE__ */ o(s, {
|
|
108
110
|
onClick: this.handleReset,
|
|
109
111
|
variant: "default",
|
|
110
|
-
children: [/* @__PURE__ */ i
|
|
112
|
+
children: [/* @__PURE__ */ a(i, { className: "mr-2 size-4" }), e("errorBoundary.tryAgain", "Try Again")]
|
|
111
113
|
}),
|
|
112
|
-
/* @__PURE__ */
|
|
114
|
+
/* @__PURE__ */ a(s, {
|
|
113
115
|
onClick: this.handleReload,
|
|
114
116
|
variant: "outline",
|
|
115
|
-
children: "Reload Page"
|
|
117
|
+
children: e("errorBoundary.reloadPage", "Reload Page")
|
|
116
118
|
}),
|
|
117
|
-
/* @__PURE__ */
|
|
119
|
+
/* @__PURE__ */ o(s, {
|
|
118
120
|
onClick: this.handleGoHome,
|
|
119
121
|
variant: "ghost",
|
|
120
|
-
children: [/* @__PURE__ */
|
|
122
|
+
children: [/* @__PURE__ */ a(r, { className: "mr-2 size-4" }), e("errorBoundary.goHome", "Go Home")]
|
|
121
123
|
})
|
|
122
124
|
]
|
|
123
125
|
}),
|
|
124
|
-
/* @__PURE__ */
|
|
126
|
+
/* @__PURE__ */ o("p", {
|
|
125
127
|
className: "text-sm text-text-secondary",
|
|
126
128
|
children: [
|
|
127
|
-
"Need help?",
|
|
129
|
+
e("errorBoundary.needHelp", "Need help?"),
|
|
128
130
|
" ",
|
|
129
|
-
/* @__PURE__ */
|
|
131
|
+
/* @__PURE__ */ a("a", {
|
|
130
132
|
href: `mailto:${this.props.supportEmail ?? "support@burdenoff.com"}`,
|
|
131
133
|
className: "text-primary hover:underline transition-colors duration-200",
|
|
132
|
-
children: "Contact Support"
|
|
134
|
+
children: e("errorBoundary.contactSupport", "Contact Support")
|
|
133
135
|
})
|
|
134
136
|
]
|
|
135
137
|
})
|
|
@@ -138,7 +140,14 @@ var s = class extends e {
|
|
|
138
140
|
}) : this.props.children;
|
|
139
141
|
}
|
|
140
142
|
};
|
|
143
|
+
function l(t) {
|
|
144
|
+
let n = e();
|
|
145
|
+
return /* @__PURE__ */ a(c, {
|
|
146
|
+
...t,
|
|
147
|
+
tr: n
|
|
148
|
+
});
|
|
149
|
+
}
|
|
141
150
|
//#endregion
|
|
142
|
-
export {
|
|
151
|
+
export { l as WorkspacesErrorBoundary };
|
|
143
152
|
|
|
144
153
|
//# sourceMappingURL=WorkspacesErrorBoundary.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkspacesErrorBoundary.js","names":[],"sources":["../../src/components/WorkspacesErrorBoundary.tsx"],"sourcesContent":["import { Component, type ReactNode } from 'react';\nimport { AlertCircle, RefreshCw, Home } from 'lucide-react';\nimport { Button } from '@burdenoff/fe-libs/ui';\n\ninterface Props {\n children: ReactNode;\n fallback?: ReactNode;\n onReset?: () => void;\n /**\n * Optional \"go home\" handler. The boundary is a class component (can't\n * call `useNavigate`), so the host shell injects a React-Router-aware\n * handler here. Falls back to `window.location.href = '/'` when omitted\n * so a host that hasn't wired this up still gets a working button.\n */\n onGoHome?: () => void;\n /**\n * Contact-support email shown in the fallback UI. Defaults to\n * `support@burdenoff.com`; hosts can override (e.g. for a tenant-branded\n * deployment).\n */\n supportEmail?: string;\n}\n\ninterface State {\n hasError: boolean;\n error: Error | null;\n errorInfo: React.ErrorInfo | null;\n}\n\n/**\n *
|
|
1
|
+
{"version":3,"file":"WorkspacesErrorBoundary.js","names":[],"sources":["../../src/components/WorkspacesErrorBoundary.tsx"],"sourcesContent":["import { Component, type ReactNode } from 'react';\nimport { AlertCircle, RefreshCw, Home } from 'lucide-react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { useTr } from '../hooks/useTr';\n\ninterface Props {\n children: ReactNode;\n fallback?: ReactNode;\n onReset?: () => void;\n /**\n * Optional \"go home\" handler. The boundary is a class component (can't\n * call `useNavigate`), so the host shell injects a React-Router-aware\n * handler here. Falls back to `window.location.href = '/'` when omitted\n * so a host that hasn't wired this up still gets a working button.\n */\n onGoHome?: () => void;\n /**\n * Contact-support email shown in the fallback UI. Defaults to\n * `support@burdenoff.com`; hosts can override (e.g. for a tenant-branded\n * deployment).\n */\n supportEmail?: string;\n}\n\ninterface ClassProps extends Props {\n tr: ReturnType<typeof useTr>;\n}\n\ninterface State {\n hasError: boolean;\n error: Error | null;\n errorInfo: React.ErrorInfo | null;\n}\n\n/**\n * Internal class boundary that receives the translator via props.\n * The public `WorkspacesErrorBoundary` wrapper is a functional component\n * so it can call `useTr()`.\n */\nclass WorkspacesErrorBoundaryClass extends Component<ClassProps, State> {\n constructor(props: ClassProps) {\n super(props);\n this.state = {\n hasError: false,\n error: null,\n errorInfo: null,\n };\n }\n\n static getDerivedStateFromError(error: Error): Partial<State> {\n return { hasError: true, error };\n }\n\n componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {\n // Log error to console in development\n if (process.env.NODE_ENV === 'development') {\n console.error('Workspaces Error Boundary caught an error:', error, errorInfo);\n }\n\n // Update state with error details\n this.setState({\n error,\n errorInfo,\n });\n\n // Forward to the shell-owned event bus per\n // ~/products/infra/infra-specs/microfrontends/47-FRONTEND-EVENT-BUS.md.\n // The shell wires Rybbit / OTel / SRE sinks; the MFE never sinks directly.\n // Window guard keeps SSR / non-browser environments safe.\n if (typeof window !== 'undefined') {\n const w = window as unknown as {\n __burdenoffEventBus?: { emit: (name: string, payload: unknown) => void };\n };\n try {\n w.__burdenoffEventBus?.emit('workspaces.error-boundary.caught', {\n source: 'microfe-workspaces',\n message: error.message,\n name: error.name,\n stack: error.stack,\n componentStack: errorInfo.componentStack,\n });\n } catch {\n // Bus failures must not mask the original render error.\n }\n }\n }\n\n handleReset = () => {\n this.setState({\n hasError: false,\n error: null,\n errorInfo: null,\n });\n\n // Call optional onReset callback\n this.props.onReset?.();\n };\n\n handleReload = () => {\n // Deliberately a full-page reload here: the React tree threw and is\n // already corrupted, so SPA navigation can't be trusted. This is the\n // one window.location call we keep in the boundary.\n window.location.reload();\n };\n\n handleGoHome = () => {\n if (this.props.onGoHome) {\n this.props.onGoHome();\n // SPA navigation doesn't remount this boundary; clear error state so\n // the new route renders instead of the fallback UI.\n this.setState({ hasError: false, error: null, errorInfo: null });\n return;\n }\n window.location.href = '/';\n };\n\n render() {\n const { tr } = this.props;\n if (this.state.hasError) {\n // Use custom fallback if provided\n if (this.props.fallback) {\n return this.props.fallback;\n }\n\n // Default error UI\n return (\n <div className=\"flex items-center justify-center min-h-[400px] p-6\">\n <div className=\"max-w-md w-full text-center space-y-6\">\n {/* Error Icon */}\n <div className=\"flex justify-center\">\n <div className=\"rounded-full bg-status-error-bg/10 p-6\">\n <AlertCircle className=\"size-12 text-status-error-text\" />\n </div>\n </div>\n\n {/* Error Message */}\n <div className=\"space-y-2\">\n <h2 className=\"text-2xl font-bold\">\n {tr('errorBoundary.title', 'Something went wrong')}\n </h2>\n <p className=\"text-text-secondary\">\n {tr(\n 'errorBoundary.description',\n 'We encountered an unexpected error. Please try refreshing the page or contact support if the problem persists.'\n )}\n </p>\n </div>\n\n {/* Error Details (Development Only) */}\n {process.env.NODE_ENV === 'development' && this.state.error && (\n <details className=\"text-left\">\n <summary className=\"cursor-pointer text-sm font-medium text-text-secondary hover:text-text-primary transition-colors duration-200\">\n {tr('errorBoundary.errorDetails', 'Error Details')}\n </summary>\n <div className=\"mt-2 p-4 bg-bg-sunken rounded-lg text-xs font-mono overflow-auto max-h-40\">\n <p className=\"font-semibold text-status-error-text\">{this.state.error.name}:</p>\n <p className=\"text-text-primary\">{this.state.error.message}</p>\n {this.state.error.stack && (\n <pre className=\"mt-2 text-text-secondary text-xs overflow-x-auto\">\n {this.state.error.stack}\n </pre>\n )}\n </div>\n </details>\n )}\n\n {/* Action Buttons */}\n <div className=\"flex flex-col sm:flex-row gap-3 justify-center\">\n <Button onClick={this.handleReset} variant=\"default\">\n <RefreshCw className=\"mr-2 size-4\" />\n {tr('errorBoundary.tryAgain', 'Try Again')}\n </Button>\n <Button onClick={this.handleReload} variant=\"outline\">\n {tr('errorBoundary.reloadPage', 'Reload Page')}\n </Button>\n <Button onClick={this.handleGoHome} variant=\"ghost\">\n <Home className=\"mr-2 size-4\" />\n {tr('errorBoundary.goHome', 'Go Home')}\n </Button>\n </div>\n\n {/* Support Link */}\n <p className=\"text-sm text-text-secondary\">\n {tr('errorBoundary.needHelp', 'Need help?')}{' '}\n <a\n href={`mailto:${this.props.supportEmail ?? 'support@burdenoff.com'}`}\n className=\"text-primary hover:underline transition-colors duration-200\"\n >\n {tr('errorBoundary.contactSupport', 'Contact Support')}\n </a>\n </p>\n </div>\n </div>\n );\n }\n\n return this.props.children;\n }\n}\n\n/**\n * Error Boundary component for Workspaces microfrontend.\n * Catches and handles runtime errors gracefully.\n *\n * The public export is a thin functional wrapper around the class boundary\n * so it can consume the i18n translator via `useTr()`.\n */\nexport function WorkspacesErrorBoundary(props: Props) {\n const tr = useTr();\n return <WorkspacesErrorBoundaryClass {...props} tr={tr} />;\n}\n"],"mappings":"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"}
|