@burdenoff/microfe-workspaces 2026.623.1 → 2026.624.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.
Files changed (73) hide show
  1. package/dist/components/CreateWorkspaceDialog.js +5 -5
  2. package/dist/components/CreateWorkspaceDialog.js.map +1 -1
  3. package/dist/components/InviteMemberDialog.js +11 -11
  4. package/dist/components/InviteMemberDialog.js.map +1 -1
  5. package/dist/components/ProjectCard.js +5 -5
  6. package/dist/components/ProjectCard.js.map +1 -1
  7. package/dist/components/ProjectMemberPickerDialog.js +14 -14
  8. package/dist/components/ProjectMemberPickerDialog.js.map +1 -1
  9. package/dist/components/WorkspaceCard.js +3 -3
  10. package/dist/components/WorkspaceCard.js.map +1 -1
  11. package/dist/components/WorkspacesErrorBoundary.js +9 -9
  12. package/dist/components/WorkspacesErrorBoundary.js.map +1 -1
  13. package/dist/components/tags/ProjectTagsWidget.js +8 -8
  14. package/dist/components/tags/ProjectTagsWidget.js.map +1 -1
  15. package/dist/pages/ActivityListPage.js +12 -12
  16. package/dist/pages/ActivityListPage.js.map +1 -1
  17. package/dist/pages/CreateWorkspacePage.js +24 -24
  18. package/dist/pages/CreateWorkspacePage.js.map +1 -1
  19. package/dist/pages/InviteAcceptancePage.js +46 -46
  20. package/dist/pages/InviteAcceptancePage.js.map +1 -1
  21. package/dist/pages/MembersListPage.js +27 -27
  22. package/dist/pages/MembersListPage.js.map +1 -1
  23. package/dist/pages/PendingInvitesPage.js +23 -23
  24. package/dist/pages/PendingInvitesPage.js.map +1 -1
  25. package/dist/pages/ProjectDetailPage.js +20 -20
  26. package/dist/pages/ProjectDetailPage.js.map +1 -1
  27. package/dist/pages/ProjectMembersPage.js +15 -15
  28. package/dist/pages/ProjectMembersPage.js.map +1 -1
  29. package/dist/pages/ProjectsListPage.js +16 -16
  30. package/dist/pages/ProjectsListPage.js.map +1 -1
  31. package/dist/pages/WorkspaceDetailPage.js +16 -16
  32. package/dist/pages/WorkspaceDetailPage.js.map +1 -1
  33. package/dist/pages/WorkspaceInvitePage.js +51 -51
  34. package/dist/pages/WorkspaceInvitePage.js.map +1 -1
  35. package/dist/pages/WorkspaceSettingsPage.js +2 -2
  36. package/dist/pages/WorkspaceSettingsPage.js.map +1 -1
  37. package/dist/pages/WorkspacesListPage.js +8 -8
  38. package/dist/pages/WorkspacesListPage.js.map +1 -1
  39. package/dist/pages/dashboard/DashboardCustomizer.js +3 -3
  40. package/dist/pages/dashboard/DashboardCustomizer.js.map +1 -1
  41. package/dist/pages/dashboard/DashboardGrid.js +2 -2
  42. package/dist/pages/dashboard/DashboardGrid.js.map +1 -1
  43. package/dist/pages/dashboard/DashboardPage.js +5 -5
  44. package/dist/pages/dashboard/DashboardPage.js.map +1 -1
  45. package/dist/pages/dashboard/WidgetWrapper.js +6 -6
  46. package/dist/pages/dashboard/WidgetWrapper.js.map +1 -1
  47. package/dist/pages/dashboard/widgets/AgentsWidget.js +6 -6
  48. package/dist/pages/dashboard/widgets/AgentsWidget.js.map +1 -1
  49. package/dist/pages/dashboard/widgets/GroupsWidget.js +3 -3
  50. package/dist/pages/dashboard/widgets/GroupsWidget.js.map +1 -1
  51. package/dist/pages/dashboard/widgets/MembersWidget.js +3 -3
  52. package/dist/pages/dashboard/widgets/MembersWidget.js.map +1 -1
  53. package/dist/pages/dashboard/widgets/ProjectsWidget.js +2 -2
  54. package/dist/pages/dashboard/widgets/ProjectsWidget.js.map +1 -1
  55. package/dist/pages/dashboard/widgets/RecentActivityWidget.js +3 -3
  56. package/dist/pages/dashboard/widgets/RecentActivityWidget.js.map +1 -1
  57. package/dist/pages/dashboard/widgets/SessionsWidget.js +4 -4
  58. package/dist/pages/dashboard/widgets/SessionsWidget.js.map +1 -1
  59. package/dist/pages/dashboard/widgets/TagCloudWidget.js +2 -2
  60. package/dist/pages/dashboard/widgets/TagCloudWidget.js.map +1 -1
  61. package/dist/pages/dashboard/widgets/TipsWidget.js +3 -3
  62. package/dist/pages/dashboard/widgets/TipsWidget.js.map +1 -1
  63. package/dist/pages/dashboard/widgets/UpcomingEventsWidget.js +2 -2
  64. package/dist/pages/dashboard/widgets/UpcomingEventsWidget.js.map +1 -1
  65. package/dist/pages/dashboard/widgets/VibesWidget.js +2 -2
  66. package/dist/pages/dashboard/widgets/VibesWidget.js.map +1 -1
  67. package/dist/pages/dashboard/widgets/WelcomeWidget.js +3 -3
  68. package/dist/pages/dashboard/widgets/WelcomeWidget.js.map +1 -1
  69. package/dist/pages/dashboard/widgets/WorkflowStatsWidget.js +4 -4
  70. package/dist/pages/dashboard/widgets/WorkflowStatsWidget.js.map +1 -1
  71. package/dist/pages/dashboard/widgets/WorkspacesWidget.js +2 -2
  72. package/dist/pages/dashboard/widgets/WorkspacesWidget.js.map +1 -1
  73. package/package.json +1 -1
@@ -23,7 +23,7 @@ function m({ workspace: m }) {
23
23
  children: [/* @__PURE__ */ o(d, { children: /* @__PURE__ */ s("div", {
24
24
  className: "flex items-center gap-3",
25
25
  children: [/* @__PURE__ */ o("div", {
26
- className: "p-3 rounded-lg bg-primary/10 transition-colors",
26
+ className: "p-3 rounded-lg bg-action-primary-bg/10 transition-colors",
27
27
  children: /* @__PURE__ */ o(r, {
28
28
  className: "text-primary",
29
29
  size: 24
@@ -42,7 +42,7 @@ function m({ workspace: m }) {
42
42
  })]
43
43
  })]
44
44
  }) }), /* @__PURE__ */ s(u, { children: [/* @__PURE__ */ s("div", {
45
- className: "flex flex-wrap gap-4 text-sm text-muted-foreground",
45
+ className: "flex flex-wrap gap-4 text-sm text-text-secondary",
46
46
  children: [typeof m.memberCount == "number" && m.memberCount > 0 && /* @__PURE__ */ s("div", {
47
47
  className: "flex items-center gap-1",
48
48
  children: [/* @__PURE__ */ o(a, {
@@ -65,7 +65,7 @@ function m({ workspace: m }) {
65
65
  ] })]
66
66
  })]
67
67
  }), m.domain && /* @__PURE__ */ o("p", {
68
- className: "mt-2 truncate text-xs text-muted-foreground",
68
+ className: "mt-2 truncate text-xs text-text-secondary",
69
69
  children: m.domain
70
70
  })] })]
71
71
  });
@@ -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-dom';\nimport { Card, CardHeader, CardTitle, CardContent, Badge } from '@burdenoff/fe-libs/ui';\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\n const openWorkspace = () => {\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 <Card\n className=\"cursor-pointer\"\n onClick={openWorkspace}\n onKeyDown={handleKeyDown}\n role=\"button\"\n tabIndex={0}\n aria-label={`Open workspace ${workspace.name}`}\n data-tour=\"workspaces-card\"\n >\n <CardHeader>\n <div className=\"flex items-center gap-3\">\n <div className=\"p-3 rounded-lg bg-primary/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-muted-foreground\">\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 {workspace.memberCount} member{workspace.memberCount === 1 ? '' : 's'}\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 {workspace.projectCount} project{workspace.projectCount === 1 ? '' : 's'}\n </span>\n </div>\n )}\n </div>\n {workspace.domain && (\n <p className=\"mt-2 truncate text-xs text-muted-foreground\">{workspace.domain}</p>\n )}\n </CardContent>\n </Card>\n );\n}\n"],"mappings":";;;;;;;;AAYA,SAAgB,EAAc,EAAE,gBAAiC;CAC/D,IAAM,IAAW,GAAa,EACxB,EAAE,cAAW,QAAQ,GAAsB,EAC3C,EAAE,yBAAsB,GAAqB,EAE7C,UAAsB;AAC1B,IAAS,EAAkB,GAAG,EAAS,GAAG,EAAU,KAAK,CAAC;;AAU5D,QACE,kBAAC,GAAD;EACE,WAAU;EACV,SAAS;EACT,YAXmB,MAA+C;AACpE,IAAI,EAAM,QAAQ,WAAW,EAAM,QAAQ,SACzC,EAAM,gBAAgB,EACtB,GAAe;;EASf,MAAK;EACL,UAAU;EACV,cAAY,kBAAkB,EAAU;EACxC,aAAU;YAPZ,CASE,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,UAAA;KACG,EAAU;KAAY;KAAQ,EAAU,gBAAgB,IAAI,KAAK;KAC7D,EAAA,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,UAAA;KACG,EAAU;KAAa;KAAS,EAAU,iBAAiB,IAAI,KAAK;KAChE,EAAA,CAAA,CACH;MAEJ;MACL,EAAU,UACT,kBAAC,KAAD;GAAG,WAAU;aAA+C,EAAU;GAAW,CAAA,CAEvE,EAAA,CAAA,CACT"}
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-dom';\nimport { Card, CardHeader, CardTitle, CardContent, Badge } from '@burdenoff/fe-libs/ui';\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\n const openWorkspace = () => {\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 <Card\n className=\"cursor-pointer\"\n onClick={openWorkspace}\n onKeyDown={handleKeyDown}\n role=\"button\"\n tabIndex={0}\n aria-label={`Open workspace ${workspace.name}`}\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 {workspace.memberCount} member{workspace.memberCount === 1 ? '' : 's'}\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 {workspace.projectCount} project{workspace.projectCount === 1 ? '' : 's'}\n </span>\n </div>\n )}\n </div>\n {workspace.domain && (\n <p className=\"mt-2 truncate text-xs text-text-secondary\">{workspace.domain}</p>\n )}\n </CardContent>\n </Card>\n );\n}\n"],"mappings":";;;;;;;;AAYA,SAAgB,EAAc,EAAE,gBAAiC;CAC/D,IAAM,IAAW,GAAa,EACxB,EAAE,cAAW,QAAQ,GAAsB,EAC3C,EAAE,yBAAsB,GAAqB,EAE7C,UAAsB;AAC1B,IAAS,EAAkB,GAAG,EAAS,GAAG,EAAU,KAAK,CAAC;;AAU5D,QACE,kBAAC,GAAD;EACE,WAAU;EACV,SAAS;EACT,YAXmB,MAA+C;AACpE,IAAI,EAAM,QAAQ,WAAW,EAAM,QAAQ,SACzC,EAAM,gBAAgB,EACtB,GAAe;;EASf,MAAK;EACL,UAAU;EACV,cAAY,kBAAkB,EAAU;EACxC,aAAU;YAPZ,CASE,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,UAAA;KACG,EAAU;KAAY;KAAQ,EAAU,gBAAgB,IAAI,KAAK;KAC7D,EAAA,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,UAAA;KACG,EAAU;KAAa;KAAS,EAAU,iBAAiB,IAAI,KAAK;KAChE,EAAA,CAAA,CACH;MAEJ;MACL,EAAU,UACT,kBAAC,KAAD;GAAG,WAAU;aAA6C,EAAU;GAAW,CAAA,CAErE,EAAA,CAAA,CACT"}
@@ -64,8 +64,8 @@ var s = class extends e {
64
64
  /* @__PURE__ */ i("div", {
65
65
  className: "flex justify-center",
66
66
  children: /* @__PURE__ */ i("div", {
67
- className: "rounded-full bg-destructive/10 p-6",
68
- children: /* @__PURE__ */ i(t, { className: "size-12 text-destructive" })
67
+ className: "rounded-full bg-status-error-bg/10 p-6",
68
+ children: /* @__PURE__ */ i(t, { className: "size-12 text-status-error-text" })
69
69
  })
70
70
  }),
71
71
  /* @__PURE__ */ a("div", {
@@ -74,28 +74,28 @@ var s = class extends e {
74
74
  className: "text-2xl font-bold",
75
75
  children: "Something went wrong"
76
76
  }), /* @__PURE__ */ i("p", {
77
- className: "text-muted-foreground",
77
+ className: "text-text-secondary",
78
78
  children: "We encountered an unexpected error. Please try refreshing the page or contact support if the problem persists."
79
79
  })]
80
80
  }),
81
81
  process.env.NODE_ENV === "development" && this.state.error && /* @__PURE__ */ a("details", {
82
82
  className: "text-left",
83
83
  children: [/* @__PURE__ */ i("summary", {
84
- className: "cursor-pointer text-sm font-medium text-muted-foreground hover:text-foreground transition-colors duration-200",
84
+ className: "cursor-pointer text-sm font-medium text-text-secondary hover:text-text-primary transition-colors duration-200",
85
85
  children: "Error Details"
86
86
  }), /* @__PURE__ */ a("div", {
87
- className: "mt-2 p-4 bg-muted rounded-lg text-xs font-mono overflow-auto max-h-40",
87
+ className: "mt-2 p-4 bg-bg-sunken rounded-lg text-xs font-mono overflow-auto max-h-40",
88
88
  children: [
89
89
  /* @__PURE__ */ a("p", {
90
- className: "font-semibold text-destructive",
90
+ className: "font-semibold text-status-error-text",
91
91
  children: [this.state.error.name, ":"]
92
92
  }),
93
93
  /* @__PURE__ */ i("p", {
94
- className: "text-foreground",
94
+ className: "text-text-primary",
95
95
  children: this.state.error.message
96
96
  }),
97
97
  this.state.error.stack && /* @__PURE__ */ i("pre", {
98
- className: "mt-2 text-muted-foreground text-xs overflow-x-auto",
98
+ className: "mt-2 text-text-secondary text-xs overflow-x-auto",
99
99
  children: this.state.error.stack
100
100
  })
101
101
  ]
@@ -122,7 +122,7 @@ var s = class extends e {
122
122
  ]
123
123
  }),
124
124
  /* @__PURE__ */ a("p", {
125
- className: "text-sm text-muted-foreground",
125
+ className: "text-sm text-text-secondary",
126
126
  children: [
127
127
  "Need help?",
128
128
  " ",
@@ -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 * Error Boundary component for Workspaces microfrontend\n * Catches and handles runtime errors gracefully\n */\nexport class WorkspacesErrorBoundary extends Component<Props, State> {\n constructor(props: Props) {\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 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-destructive/10 p-6\">\n <AlertCircle className=\"size-12 text-destructive\" />\n </div>\n </div>\n\n {/* Error Message */}\n <div className=\"space-y-2\">\n <h2 className=\"text-2xl font-bold\">Something went wrong</h2>\n <p className=\"text-muted-foreground\">\n We encountered an unexpected error. Please try refreshing the page or contact\n support if the problem persists.\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-muted-foreground hover:text-foreground transition-colors duration-200\">\n Error Details\n </summary>\n <div className=\"mt-2 p-4 bg-muted rounded-lg text-xs font-mono overflow-auto max-h-40\">\n <p className=\"font-semibold text-destructive\">{this.state.error.name}:</p>\n <p className=\"text-foreground\">{this.state.error.message}</p>\n {this.state.error.stack && (\n <pre className=\"mt-2 text-muted-foreground 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 Try Again\n </Button>\n <Button onClick={this.handleReload} variant=\"outline\">\n Reload Page\n </Button>\n <Button onClick={this.handleGoHome} variant=\"ghost\">\n <Home className=\"mr-2 size-4\" />\n Go Home\n </Button>\n </div>\n\n {/* Support Link */}\n <p className=\"text-sm text-muted-foreground\">\n 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 Contact Support\n </a>\n </p>\n </div>\n </div>\n );\n }\n\n return this.props.children;\n }\n}\n"],"mappings":";;;;;AAiCA,IAAa,IAAb,cAA6C,EAAwB;CACnE,YAAY,GAAc;AAExB,EADA,MAAM,EAAM,EACZ,KAAK,QAAQ;GACX,UAAU;GACV,OAAO;GACP,WAAW;GACZ;;CAGH,OAAO,yBAAyB,GAA8B;AAC5D,SAAO;GAAE,UAAU;GAAM;GAAO;;CAGlC,kBAAkB,GAAc,GAA4B;AAgB1D,MAdA,QAAA,IAAA,aAA6B,iBAC3B,QAAQ,MAAM,8CAA8C,GAAO,EAAU,EAI/E,KAAK,SAAS;GACZ;GACA;GACD,CAAC,EAME,OAAO,SAAW,KAAa;GACjC,IAAM,IAAI;AAGV,OAAI;AACF,MAAE,qBAAqB,KAAK,oCAAoC;KAC9D,QAAQ;KACR,SAAS,EAAM;KACf,MAAM,EAAM;KACZ,OAAO,EAAM;KACb,gBAAgB,EAAU;KAC3B,CAAC;WACI;;;CAMZ,oBAAoB;AAQlB,EAPA,KAAK,SAAS;GACZ,UAAU;GACV,OAAO;GACP,WAAW;GACZ,CAAC,EAGF,KAAK,MAAM,WAAW;;CAGxB,qBAAqB;AAInB,SAAO,SAAS,QAAQ;;CAG1B,qBAAqB;AACnB,MAAI,KAAK,MAAM,UAAU;AAIvB,GAHA,KAAK,MAAM,UAAU,EAGrB,KAAK,SAAS;IAAE,UAAU;IAAO,OAAO;IAAM,WAAW;IAAM,CAAC;AAChE;;AAEF,SAAO,SAAS,OAAO;;CAGzB,SAAS;AA2EP,SA1EI,KAAK,MAAM,WAET,KAAK,MAAM,WACN,KAAK,MAAM,WAKlB,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,OAAD;IAAK,WAAU;cAAf;KAEE,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD,EAAa,WAAU,4BAA6B,CAAA;OAChD,CAAA;MACF,CAAA;KAGN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBAAqB;OAAyB,CAAA,EAC5D,kBAAC,KAAD;OAAG,WAAU;iBAAwB;OAGjC,CAAA,CACA;;8BAGoB,iBAAiB,KAAK,MAAM,SACpD,kBAAC,WAAD;MAAS,WAAU;gBAAnB,CACE,kBAAC,WAAD;OAAS,WAAU;iBAAgH;OAEzH,CAAA,EACV,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,KAAD;SAAG,WAAU;mBAAb,CAA+C,KAAK,MAAM,MAAM,MAAK,IAAK;;QAC1E,kBAAC,KAAD;SAAG,WAAU;mBAAmB,KAAK,MAAM,MAAM;SAAY,CAAA;QAC5D,KAAK,MAAM,MAAM,SAChB,kBAAC,OAAD;SAAK,WAAU;mBACZ,KAAK,MAAM,MAAM;SACd,CAAA;QAEJ;SACE;;KAIZ,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,GAAD;QAAQ,SAAS,KAAK;QAAa,SAAQ;kBAA3C,CACE,kBAAC,GAAD,EAAW,WAAU,eAAgB,CAAA,EAAA,YAE9B;;OACT,kBAAC,GAAD;QAAQ,SAAS,KAAK;QAAc,SAAQ;kBAAU;QAE7C,CAAA;OACT,kBAAC,GAAD;QAAQ,SAAS,KAAK;QAAc,SAAQ;kBAA5C,CACE,kBAAC,GAAD,EAAM,WAAU,eAAgB,CAAA,EAAA,UAEzB;;OACL;;KAGN,kBAAC,KAAD;MAAG,WAAU;gBAAb;OAA6C;OAChC;OACX,kBAAC,KAAD;QACE,MAAM,UAAU,KAAK,MAAM,gBAAgB;QAC3C,WAAU;kBACX;QAEG,CAAA;OACF;;KACA;;GACF,CAAA,GAIH,KAAK,MAAM"}
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 * Error Boundary component for Workspaces microfrontend\n * Catches and handles runtime errors gracefully\n */\nexport class WorkspacesErrorBoundary extends Component<Props, State> {\n constructor(props: Props) {\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 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\">Something went wrong</h2>\n <p className=\"text-text-secondary\">\n We encountered an unexpected error. Please try refreshing the page or contact\n support if the problem persists.\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 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 Try Again\n </Button>\n <Button onClick={this.handleReload} variant=\"outline\">\n Reload Page\n </Button>\n <Button onClick={this.handleGoHome} variant=\"ghost\">\n <Home className=\"mr-2 size-4\" />\n Go Home\n </Button>\n </div>\n\n {/* Support Link */}\n <p className=\"text-sm text-text-secondary\">\n 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 Contact Support\n </a>\n </p>\n </div>\n </div>\n );\n }\n\n return this.props.children;\n }\n}\n"],"mappings":";;;;;AAiCA,IAAa,IAAb,cAA6C,EAAwB;CACnE,YAAY,GAAc;AAExB,EADA,MAAM,EAAM,EACZ,KAAK,QAAQ;GACX,UAAU;GACV,OAAO;GACP,WAAW;GACZ;;CAGH,OAAO,yBAAyB,GAA8B;AAC5D,SAAO;GAAE,UAAU;GAAM;GAAO;;CAGlC,kBAAkB,GAAc,GAA4B;AAgB1D,MAdA,QAAA,IAAA,aAA6B,iBAC3B,QAAQ,MAAM,8CAA8C,GAAO,EAAU,EAI/E,KAAK,SAAS;GACZ;GACA;GACD,CAAC,EAME,OAAO,SAAW,KAAa;GACjC,IAAM,IAAI;AAGV,OAAI;AACF,MAAE,qBAAqB,KAAK,oCAAoC;KAC9D,QAAQ;KACR,SAAS,EAAM;KACf,MAAM,EAAM;KACZ,OAAO,EAAM;KACb,gBAAgB,EAAU;KAC3B,CAAC;WACI;;;CAMZ,oBAAoB;AAQlB,EAPA,KAAK,SAAS;GACZ,UAAU;GACV,OAAO;GACP,WAAW;GACZ,CAAC,EAGF,KAAK,MAAM,WAAW;;CAGxB,qBAAqB;AAInB,SAAO,SAAS,QAAQ;;CAG1B,qBAAqB;AACnB,MAAI,KAAK,MAAM,UAAU;AAIvB,GAHA,KAAK,MAAM,UAAU,EAGrB,KAAK,SAAS;IAAE,UAAU;IAAO,OAAO;IAAM,WAAW;IAAM,CAAC;AAChE;;AAEF,SAAO,SAAS,OAAO;;CAGzB,SAAS;AA2EP,SA1EI,KAAK,MAAM,WAET,KAAK,MAAM,WACN,KAAK,MAAM,WAKlB,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,OAAD;IAAK,WAAU;cAAf;KAEE,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD,EAAa,WAAU,kCAAmC,CAAA;OACtD,CAAA;MACF,CAAA;KAGN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBAAqB;OAAyB,CAAA,EAC5D,kBAAC,KAAD;OAAG,WAAU;iBAAsB;OAG/B,CAAA,CACA;;8BAGoB,iBAAiB,KAAK,MAAM,SACpD,kBAAC,WAAD;MAAS,WAAU;gBAAnB,CACE,kBAAC,WAAD;OAAS,WAAU;iBAAgH;OAEzH,CAAA,EACV,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,KAAD;SAAG,WAAU;mBAAb,CAAqD,KAAK,MAAM,MAAM,MAAK,IAAK;;QAChF,kBAAC,KAAD;SAAG,WAAU;mBAAqB,KAAK,MAAM,MAAM;SAAY,CAAA;QAC9D,KAAK,MAAM,MAAM,SAChB,kBAAC,OAAD;SAAK,WAAU;mBACZ,KAAK,MAAM,MAAM;SACd,CAAA;QAEJ;SACE;;KAIZ,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,GAAD;QAAQ,SAAS,KAAK;QAAa,SAAQ;kBAA3C,CACE,kBAAC,GAAD,EAAW,WAAU,eAAgB,CAAA,EAAA,YAE9B;;OACT,kBAAC,GAAD;QAAQ,SAAS,KAAK;QAAc,SAAQ;kBAAU;QAE7C,CAAA;OACT,kBAAC,GAAD;QAAQ,SAAS,KAAK;QAAc,SAAQ;kBAA5C,CACE,kBAAC,GAAD,EAAM,WAAU,eAAgB,CAAA,EAAA,UAEzB;;OACL;;KAGN,kBAAC,KAAD;MAAG,WAAU;gBAAb;OAA2C;OAC9B;OACX,kBAAC,KAAD;QACE,MAAM,UAAU,KAAK,MAAM,gBAAgB;QAC3C,WAAU;kBACX;QAEG,CAAA;OACF;;KACA;;GACF,CAAA,GAIH,KAAK,MAAM"}
@@ -164,10 +164,10 @@ function w({ entityType: g, entityId: w, workspaceId: se, tags: T, onAdd: E, onR
164
164
  children: [/* @__PURE__ */ s(re, {
165
165
  type: "button",
166
166
  "aria-label": "Add tag",
167
- className: h("inline-flex items-center gap-1 px-2 py-0.5 rounded-full border border-dashed", "border-border text-muted-foreground text-xs", "hover:border-foreground hover:text-foreground transition-colors", "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"),
167
+ className: h("inline-flex items-center gap-1 px-2 py-0.5 rounded-full border border-dashed", "border-border-subtle text-text-secondary text-xs", "hover:border-foreground hover:text-text-primary transition-colors", "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]"),
168
168
  children: [/* @__PURE__ */ o(a, { className: "size-3" }), /* @__PURE__ */ o("span", { children: T.length > 0 ? "Add" : "Add tags" })]
169
169
  }), /* @__PURE__ */ o(m, {
170
- className: "z-50 w-72 max-w-[calc(100vw-2rem)] border-border/80 bg-popover p-0 shadow-xl",
170
+ className: "z-50 w-72 max-w-[calc(100vw-2rem)] border-border-subtle/80 bg-popover p-0 shadow-xl",
171
171
  align: "start",
172
172
  side: "bottom",
173
173
  sideOffset: 8,
@@ -180,14 +180,14 @@ function w({ entityType: g, entityId: w, workspaceId: se, tags: T, onAdd: E, onR
180
180
  placeholder: "Search or create a tag...",
181
181
  value: j,
182
182
  onValueChange: de,
183
- className: "h-10 border-0 bg-transparent pr-9 text-sm outline-none placeholder:text-muted-foreground"
184
- }), G && /* @__PURE__ */ o(i, { className: "absolute right-3 top-1/2 size-3.5 -translate-y-1/2 animate-spin text-muted-foreground" })]
183
+ className: "h-10 border-0 bg-transparent pr-9 text-sm outline-none placeholder:text-text-secondary"
184
+ }), G && /* @__PURE__ */ o(i, { className: "absolute right-3 top-1/2 size-3.5 -translate-y-1/2 animate-spin text-text-secondary" })]
185
185
  }),
186
186
  /* @__PURE__ */ s(f, {
187
187
  className: "max-h-48 overflow-y-auto",
188
188
  children: [
189
189
  j.trim() === "" && /* @__PURE__ */ o(c, {
190
- className: "py-4 text-center text-xs text-muted-foreground",
190
+ className: "py-4 text-center text-xs text-text-secondary",
191
191
  children: "Type to search or create a tag"
192
192
  }),
193
193
  K.length > 0 && /* @__PURE__ */ o(l, { children: K.map((e) => /* @__PURE__ */ s(d, {
@@ -219,17 +219,17 @@ function w({ entityType: g, entityId: w, workspaceId: se, tags: T, onAdd: E, onR
219
219
  })]
220
220
  }) }),
221
221
  j.trim() !== "" && !G && K.length === 0 && !$ && /* @__PURE__ */ o(c, {
222
- className: "py-4 text-center text-xs text-muted-foreground",
222
+ className: "py-4 text-center text-xs text-text-secondary",
223
223
  children: "No tags found"
224
224
  }),
225
225
  N && /* @__PURE__ */ o("div", {
226
- className: "px-3 py-2 text-xs text-destructive border-t border-border",
226
+ className: "px-3 py-2 text-xs text-status-error-text border-t border-border-subtle",
227
227
  children: N
228
228
  })
229
229
  ]
230
230
  }),
231
231
  !U && /* @__PURE__ */ s("div", {
232
- className: "border-t border-border px-3 py-2 text-xs text-muted-foreground flex items-center gap-1",
232
+ className: "border-t border-border-subtle px-3 py-2 text-xs text-text-secondary flex items-center gap-1",
233
233
  children: [
234
234
  /* @__PURE__ */ o(te, { className: "size-3" }),
235
235
  "Maximum ",
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectTagsWidget.js","names":[],"sources":["../../../src/components/tags/ProjectTagsWidget.tsx"],"sourcesContent":["/**\n * ProjectTagsWidget\n *\n * Local replacement for `@burdenoff/fe-libs/tag` `TagsWidget`, used until\n * fe-libs ships a fix for the non-clickable `AddTagTrigger` (the published\n * trigger swallows the props Radix Slot tries to forward via\n * `<PopoverTrigger asChild>`, so the popover never opens).\n *\n * This component bypasses the broken trigger by rendering a regular\n * `<PopoverTrigger>` (no `asChild`) so Radix mounts the click-handling\n * button itself. Internals (search, create, assign, remove) mirror the\n * fe-libs `TagsWidget` so the data path through wspace-tags-svc is\n * identical.\n *\n * TODO(fe-libs): once `@burdenoff/fe-libs/tag` `AddTagTrigger` forwards\n * props/ref to the underlying button, delete this file and switch back\n * to `<TagsWidget>` from `@burdenoff/fe-libs/tag`.\n */\n\nimport { gql, type ApolloClient } from '@apollo/client';\nimport { useLazyQuery, useMutation } from '@apollo/client/react';\nimport { Loader2, Plus, Tag as TagIcon, X } from 'lucide-react';\nimport { useCallback, useEffect, useRef, useState } from 'react';\n\nimport { type TagItem } from '@burdenoff/fe-libs/tag';\nimport { TagBadge } from '@burdenoff/fe-libs/tag';\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport { useGatewayClient } from '@burdenoff/fe-libs/shared/graphql';\nimport { cn } from '@burdenoff/fe-libs/shared/utils';\n\n// --- GraphQL ----------------------------------------------------------------\n\ninterface VocabResult {\n id: string;\n key: string;\n name: string;\n}\n\nconst SEARCH_TAGS = gql`\n query ProjectTagsWidget_SearchTags($query: String!, $vocabularyId: ID, $limit: Int) {\n searchTags(query: $query, vocabularyId: $vocabularyId, limit: $limit) {\n id\n key\n label\n value\n vocabularyId\n parentId\n hierarchyPath\n color\n icon\n description\n vocabulary {\n key\n }\n }\n }\n`;\n\nconst GET_VOCABULARY_BY_KEY = gql`\n query ProjectTagsWidget_GetVocabularyByKey($key: String!) {\n getVocabularyByKey(key: $key) {\n id\n key\n name\n }\n }\n`;\n\nconst CREATE_VOCABULARY = gql`\n mutation ProjectTagsWidget_CreateVocabulary($input: CreateVocabularyInput!) {\n createVocabulary(input: $input) {\n id\n key\n name\n }\n }\n`;\n\nconst CREATE_TAG = gql`\n mutation ProjectTagsWidget_CreateTag($input: CreateTagInput!) {\n createTag(input: $input) {\n id\n key\n label\n value\n vocabularyId\n parentId\n hierarchyPath\n color\n icon\n vocabulary {\n key\n }\n }\n }\n`;\n\nconst DEFAULT_VOCAB_KEY = 'general';\n\nfunction isTagItem(value: unknown): value is TagItem {\n if (!value || typeof value !== 'object') return false;\n const tag = value as Partial<Record<keyof TagItem, unknown>>;\n return typeof tag.id === 'string' && typeof tag.key === 'string' && typeof tag.label === 'string';\n}\n\n// --- Props ------------------------------------------------------------------\n\nexport interface ProjectTagsWidgetProps {\n /** Entity type recorded on the assigned-tag projection (e.g. \"Project\"). */\n entityType: string;\n /** Entity id (project id). */\n entityId: string;\n /** Workspace scope — widget renders nothing when empty. */\n workspaceId?: string;\n /** Controlled tag list owned by the parent. */\n tags: TagItem[];\n /** Persist a newly assigned tag on the entity. */\n onAdd: (tag: TagItem) => void | Promise<void>;\n /** Persist a tag removal on the entity. */\n onRemove: (tagId: string) => void | Promise<void>;\n /** Allow creating new tags on the fly. Default: true. */\n allowCreate?: boolean;\n /** Cap on assigned tags. Default 20. */\n maxTags?: number;\n className?: string;\n}\n\n// --- Component --------------------------------------------------------------\n\nexport function ProjectTagsWidget({\n entityType: _entityType,\n entityId,\n workspaceId,\n tags,\n onAdd,\n onRemove,\n allowCreate = true,\n maxTags = 20,\n className,\n}: ProjectTagsWidgetProps) {\n const [open, setOpen] = useState(false);\n const [search, setSearch] = useState('');\n const [createError, setCreateError] = useState<string | null>(null);\n const [adding, setAdding] = useState(false);\n const [resolvedVocabId, setResolvedVocabId] = useState<string | undefined>();\n const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n // Bun may install Apollo twice when fe-libs is consumed as a package; the runtime client is compatible.\n const wsClient = useGatewayClient('workspace') as unknown as ApolloClient;\n\n const skip = !workspaceId || !entityId;\n\n const assignedTagIds = new Set(tags.map((t) => t.id));\n const canAddMore = tags.length < maxTags;\n\n const [runSearch, { data: searchData, loading: searching }] = useLazyQuery<{\n searchTags: TagItem[];\n }>(SEARCH_TAGS, { client: wsClient });\n\n const searchResults = (searchData?.searchTags ?? []).filter(\n (tag): tag is TagItem => isTagItem(tag) && !assignedTagIds.has(tag.id)\n );\n\n const [createTag, { loading: creating }] = useMutation<{\n createTag: TagItem;\n }>(CREATE_TAG, { client: wsClient });\n\n const [fetchVocabByKey] = useLazyQuery<{\n getVocabularyByKey: VocabResult | null;\n }>(GET_VOCABULARY_BY_KEY, { client: wsClient });\n\n const [createVocabulary] = useMutation<{\n createVocabulary: VocabResult;\n }>(CREATE_VOCABULARY, { client: wsClient });\n\n const resolveVocabId = useCallback(async (): Promise<string> => {\n if (resolvedVocabId) return resolvedVocabId;\n\n const res = await fetchVocabByKey({ variables: { key: DEFAULT_VOCAB_KEY } });\n const existing = res.data?.getVocabularyByKey;\n if (existing) {\n setResolvedVocabId(existing.id);\n return existing.id;\n }\n\n const created = await createVocabulary({\n variables: {\n input: {\n key: DEFAULT_VOCAB_KEY,\n name: 'General',\n description: 'Default vocabulary for general-purpose tags',\n scope: 'WORKSPACE',\n allowCustomTags: true,\n },\n },\n });\n const newVocab = created.data?.createVocabulary;\n if (!newVocab) throw new Error('Failed to create default vocabulary');\n setResolvedVocabId(newVocab.id);\n return newVocab.id;\n }, [resolvedVocabId, fetchVocabByKey, createVocabulary]);\n\n const handleSearchChange = useCallback(\n (value: string) => {\n setSearch(value);\n if (createError) setCreateError(null);\n if (debounceRef.current) clearTimeout(debounceRef.current);\n if (!value.trim()) return;\n debounceRef.current = setTimeout(() => {\n void runSearch({\n variables: { query: value, vocabularyId: resolvedVocabId, limit: 10 },\n });\n }, 250);\n },\n [runSearch, resolvedVocabId, createError]\n );\n\n useEffect(() => {\n return () => {\n if (debounceRef.current) clearTimeout(debounceRef.current);\n };\n }, []);\n\n const handleAssign = useCallback(\n async (tag: TagItem) => {\n if (!canAddMore || adding) return;\n setAdding(true);\n try {\n const normalizedTag: TagItem = {\n ...tag,\n vocabularyKey: tag.vocabularyKey ?? tag.vocabulary?.key ?? null,\n };\n await onAdd(normalizedTag);\n setSearch('');\n setOpen(false);\n } finally {\n setAdding(false);\n }\n },\n [onAdd, canAddMore, adding]\n );\n\n const handleCreate = useCallback(async () => {\n if (!allowCreate || !search.trim() || creating) return;\n setCreateError(null);\n try {\n const label = search.trim();\n const key = label\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-|-$/g, '');\n if (!key) {\n setCreateError('Must contain at least one letter or number');\n return;\n }\n const vocabId = await resolveVocabId();\n const res = await createTag({\n variables: { input: { key, label, vocabularyId: vocabId } },\n });\n const created = res.data?.createTag;\n if (!isTagItem(created)) throw new Error('No data returned');\n await handleAssign(created);\n } catch (err) {\n setCreateError(err instanceof Error ? err.message : 'Failed to create tag');\n }\n }, [allowCreate, search, creating, resolveVocabId, createTag, handleAssign]);\n\n const handleRemove = useCallback(\n async (tagId: string) => {\n await onRemove(tagId);\n },\n [onRemove]\n );\n\n if (skip) return null;\n\n const showCreateOption = Boolean(\n allowCreate && search.trim() && !searching && searchResults.length === 0\n );\n\n return (\n <div className={cn('flex flex-wrap items-center gap-1.5', className)}>\n {tags.map((tag) => (\n <TagBadge\n key={tag.id}\n id={tag.id}\n label={tag.label}\n color={tag.color ?? undefined}\n icon={tag.icon ?? undefined}\n description={tag.description ?? undefined}\n removable\n onRemove={() => void handleRemove(tag.id)}\n size=\"sm\"\n />\n ))}\n\n {canAddMore && (\n <Popover open={open} onOpenChange={setOpen}>\n {/*\n Intentionally do NOT use `asChild` here — the fe-libs\n `AddTagTrigger` swallows the props Radix forwards, leaving the\n trigger non-clickable. Letting Radix render its own button\n restores the click handler.\n */}\n <PopoverTrigger\n type=\"button\"\n aria-label=\"Add tag\"\n className={cn(\n 'inline-flex items-center gap-1 px-2 py-0.5 rounded-full border border-dashed',\n 'border-border text-muted-foreground text-xs',\n 'hover:border-foreground hover:text-foreground transition-colors',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring'\n )}\n >\n <TagIcon className=\"size-3\" />\n <span>{tags.length > 0 ? 'Add' : 'Add tags'}</span>\n </PopoverTrigger>\n\n <PopoverContent\n className=\"z-50 w-72 max-w-[calc(100vw-2rem)] border-border/80 bg-popover p-0 shadow-xl\"\n align=\"start\"\n side=\"bottom\"\n sideOffset={8}\n >\n <Command shouldFilter={false}>\n <div className=\"relative\">\n <CommandInput\n placeholder=\"Search or create a tag...\"\n value={search}\n onValueChange={handleSearchChange}\n className=\"h-10 border-0 bg-transparent pr-9 text-sm outline-none placeholder:text-muted-foreground\"\n />\n {searching && (\n <Loader2 className=\"absolute right-3 top-1/2 size-3.5 -translate-y-1/2 animate-spin text-muted-foreground\" />\n )}\n </div>\n\n <CommandList className=\"max-h-48 overflow-y-auto\">\n {search.trim() === '' && (\n <CommandEmpty className=\"py-4 text-center text-xs text-muted-foreground\">\n Type to search or create a tag\n </CommandEmpty>\n )}\n\n {searchResults.length > 0 && (\n <CommandGroup>\n {searchResults.map((tag) => (\n <CommandItem\n key={tag.id}\n value={tag.id}\n onSelect={() => void handleAssign(tag)}\n className=\"flex items-center gap-2 px-3 py-2 cursor-pointer\"\n disabled={adding}\n >\n {tag.icon && <span className=\"text-sm\">{tag.icon}</span>}\n <span\n className=\"truncate text-sm\"\n style={tag.color ? { color: tag.color } : undefined}\n >\n {tag.label}\n </span>\n {adding && <Loader2 className=\"ml-auto size-3 animate-spin\" />}\n </CommandItem>\n ))}\n </CommandGroup>\n )}\n\n {showCreateOption && (\n <CommandGroup>\n <CommandItem\n value={`__create__${search}`}\n onSelect={() => void handleCreate()}\n className=\"flex items-center gap-2 px-3 py-2 cursor-pointer text-primary\"\n disabled={creating}\n >\n {creating ? (\n <Loader2 className=\"size-3.5 animate-spin\" />\n ) : (\n <Plus className=\"size-3.5\" />\n )}\n <span className=\"text-sm truncate\">\n {creating ? 'Creating...' : `Create \"${search}\"`}\n </span>\n </CommandItem>\n </CommandGroup>\n )}\n\n {search.trim() !== '' &&\n !searching &&\n searchResults.length === 0 &&\n !showCreateOption && (\n <CommandEmpty className=\"py-4 text-center text-xs text-muted-foreground\">\n No tags found\n </CommandEmpty>\n )}\n\n {createError && (\n <div className=\"px-3 py-2 text-xs text-destructive border-t border-border\">\n {createError}\n </div>\n )}\n </CommandList>\n\n {!canAddMore && (\n <div className=\"border-t border-border px-3 py-2 text-xs text-muted-foreground flex items-center gap-1\">\n <X className=\"size-3\" />\n Maximum {maxTags} tags reached\n </div>\n )}\n </Command>\n </PopoverContent>\n </Popover>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;AAgDA,IAAM,IAAc,CAAG;;;;;;;;;;;;;;;;;;GAoBjB,IAAwB,CAAG;;;;;;;;GAU3B,KAAoB,CAAG;;;;;;;;GAUvB,KAAa,CAAG;;;;;;;;;;;;;;;;;GAmBhB,IAAoB;AAE1B,SAAS,EAAU,GAAkC;AACnD,KAAI,CAAC,KAAS,OAAO,KAAU,SAAU,QAAO;CAChD,IAAM,IAAM;AACZ,QAAO,OAAO,EAAI,MAAO,YAAY,OAAO,EAAI,OAAQ,YAAY,OAAO,EAAI,SAAU;;AA2B3F,SAAgB,EAAkB,EAChC,YAAY,GACZ,aACA,iBACA,SACA,UACA,aACA,iBAAc,IACd,aAAU,IACV,iBACyB;CACzB,IAAM,CAAC,IAAM,KAAW,EAAS,GAAM,EACjC,CAAC,GAAQ,KAAa,EAAS,GAAG,EAClC,CAAC,GAAa,KAAkB,EAAwB,KAAK,EAC7D,CAAC,GAAQ,KAAa,EAAS,GAAM,EACrC,CAAC,GAAiB,KAAsB,GAA8B,EACtE,IAAc,EAA6C,KAAK,EAGhE,IAAW,GAAiB,YAAY,EAExC,IAAO,CAAC,MAAe,CAAC,GAExB,IAAiB,IAAI,IAAI,EAAK,KAAK,MAAM,EAAE,GAAG,CAAC,EAC/C,IAAa,EAAK,SAAS,GAE3B,CAAC,GAAW,EAAE,MAAM,IAAY,SAAS,OAAe,EAE3D,GAAa,EAAE,QAAQ,GAAU,CAAC,EAE/B,KAAiB,IAAY,cAAc,EAAE,EAAE,QAClD,MAAwB,EAAU,EAAI,IAAI,CAAC,EAAe,IAAI,EAAI,GAAG,CACvE,EAEK,CAAC,GAAW,EAAE,SAAS,OAAc,EAExC,IAAY,EAAE,QAAQ,GAAU,CAAC,EAE9B,CAAC,KAAmB,EAEvB,GAAuB,EAAE,QAAQ,GAAU,CAAC,EAEzC,CAAC,KAAoB,EAExB,IAAmB,EAAE,QAAQ,GAAU,CAAC,EAErC,IAAiB,EAAY,YAA6B;AAC9D,MAAI,EAAiB,QAAO;EAG5B,IAAM,KADM,MAAM,EAAgB,EAAE,WAAW,EAAE,KAAK,GAAmB,EAAE,CAAC,EACvD,MAAM;AAC3B,MAAI,EAEF,QADA,EAAmB,EAAS,GAAG,EACxB,EAAS;EAclB,IAAM,KAXU,MAAM,EAAiB,EACrC,WAAW,EACT,OAAO;GACL,KAAK;GACL,MAAM;GACN,aAAa;GACb,OAAO;GACP,iBAAiB;GAClB,EACF,EACF,CAAC,EACuB,MAAM;AAC/B,MAAI,CAAC,EAAU,OAAU,MAAM,sCAAsC;AAErE,SADA,EAAmB,EAAS,GAAG,EACxB,EAAS;IACf;EAAC;EAAiB;EAAiB;EAAiB,CAAC,EAElD,KAAqB,GACxB,MAAkB;AACjB,IAAU,EAAM,EACZ,KAAa,EAAe,KAAK,EACjC,EAAY,WAAS,aAAa,EAAY,QAAQ,EACrD,EAAM,MAAM,KACjB,EAAY,UAAU,iBAAiB;AAChC,KAAU,EACb,WAAW;IAAE,OAAO;IAAO,cAAc;IAAiB,OAAO;IAAI,EACtE,CAAC;KACD,IAAI;IAET;EAAC;EAAW;EAAiB;EAAY,CAC1C;AAED,eACe;AACX,EAAI,EAAY,WAAS,aAAa,EAAY,QAAQ;IAE3D,EAAE,CAAC;CAEN,IAAM,IAAe,EACnB,OAAO,MAAiB;AAClB,SAAC,KAAc,IACnB;KAAU,GAAK;AACf,OAAI;AAOF,IAFA,MAAM,EAJyB;KAC7B,GAAG;KACH,eAAe,EAAI,iBAAiB,EAAI,YAAY,OAAO;KAC5D,CACyB,EAC1B,EAAU,GAAG,EACb,EAAQ,GAAM;aACN;AACR,MAAU,GAAM;;;IAGpB;EAAC;EAAO;EAAY;EAAO,CAC5B,EAEK,KAAe,EAAY,YAAY;AACvC,SAAC,KAAe,CAAC,EAAO,MAAM,IAAI,IACtC;KAAe,KAAK;AACpB,OAAI;IACF,IAAM,IAAQ,EAAO,MAAM,EACrB,IAAM,EACT,aAAa,CACb,QAAQ,eAAe,IAAI,CAC3B,QAAQ,UAAU,GAAG;AACxB,QAAI,CAAC,GAAK;AACR,OAAe,6CAA6C;AAC5D;;IAMF,IAAM,KAHM,MAAM,EAAU,EAC1B,WAAW,EAAE,OAAO;KAAE;KAAK;KAAO,cAFpB,MAAM,GAAgB;KAEqB,EAAE,EAC5D,CAAC,EACkB,MAAM;AAC1B,QAAI,CAAC,EAAU,EAAQ,CAAE,OAAU,MAAM,mBAAmB;AAC5D,UAAM,EAAa,EAAQ;YACpB,GAAK;AACZ,MAAe,aAAe,QAAQ,EAAI,UAAU,uBAAuB;;;IAE5E;EAAC;EAAa;EAAQ;EAAU;EAAgB;EAAW;EAAa,CAAC,EAEtE,KAAe,EACnB,OAAO,MAAkB;AACvB,QAAM,EAAS,EAAM;IAEvB,CAAC,EAAS,CACX;AAED,KAAI,EAAM,QAAO;CAEjB,IAAM,IAAmB,GACvB,KAAe,EAAO,MAAM,IAAI,CAAC,KAAa,EAAc,WAAW;AAGzE,QACE,kBAAC,OAAD;EAAK,WAAW,EAAG,uCAAuC,GAAU;YAApE,CACG,EAAK,KAAK,MACT,kBAAC,GAAD;GAEE,IAAI,EAAI;GACR,OAAO,EAAI;GACX,OAAO,EAAI,SAAS,KAAA;GACpB,MAAM,EAAI,QAAQ,KAAA;GAClB,aAAa,EAAI,eAAe,KAAA;GAChC,WAAA;GACA,gBAAgB,KAAK,GAAa,EAAI,GAAG;GACzC,MAAK;GACL,EATK,EAAI,GAST,CACF,EAED,KACC,kBAAC,GAAD;GAAe;GAAM,cAAc;aAAnC,CAOE,kBAAC,IAAD;IACE,MAAK;IACL,cAAW;IACX,WAAW,EACT,gFACA,+CACA,mEACA,0EACD;cARH,CAUE,kBAAC,GAAD,EAAS,WAAU,UAAW,CAAA,EAC9B,kBAAC,QAAD,EAAA,UAAO,EAAK,SAAS,IAAI,QAAQ,YAAkB,CAAA,CACpC;OAEjB,kBAAC,GAAD;IACE,WAAU;IACV,OAAM;IACN,MAAK;IACL,YAAY;cAEZ,kBAAC,IAAD;KAAS,cAAc;eAAvB;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QACE,aAAY;QACZ,OAAO;QACP,eAAe;QACf,WAAU;QACV,CAAA,EACD,KACC,kBAAC,GAAD,EAAS,WAAU,yFAA0F,CAAA,CAE3G;;MAEN,kBAAC,GAAD;OAAa,WAAU;iBAAvB;QACG,EAAO,MAAM,KAAK,MACjB,kBAAC,GAAD;SAAc,WAAU;mBAAiD;SAE1D,CAAA;QAGhB,EAAc,SAAS,KACtB,kBAAC,GAAD,EAAA,UACG,EAAc,KAAK,MAClB,kBAAC,GAAD;SAEE,OAAO,EAAI;SACX,gBAAgB,KAAK,EAAa,EAAI;SACtC,WAAU;SACV,UAAU;mBALZ;UAOG,EAAI,QAAQ,kBAAC,QAAD;WAAM,WAAU;qBAAW,EAAI;WAAY,CAAA;UACxD,kBAAC,QAAD;WACE,WAAU;WACV,OAAO,EAAI,QAAQ,EAAE,OAAO,EAAI,OAAO,GAAG,KAAA;qBAEzC,EAAI;WACA,CAAA;UACN,KAAU,kBAAC,GAAD,EAAS,WAAU,+BAAgC,CAAA;UAClD;WAdP,EAAI,GAcG,CACd,EACW,CAAA;QAGhB,KACC,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;SACE,OAAO,aAAa;SACpB,gBAAgB,KAAK,IAAc;SACnC,WAAU;SACV,UAAU;mBAJZ,CAMG,IACC,kBAAC,GAAD,EAAS,WAAU,yBAA0B,CAAA,GAE7C,kBAAC,IAAD,EAAM,WAAU,YAAa,CAAA,EAE/B,kBAAC,QAAD;UAAM,WAAU;oBACb,IAAW,gBAAgB,WAAW,EAAO;UACzC,CAAA,CACK;YACD,CAAA;QAGhB,EAAO,MAAM,KAAK,MACjB,CAAC,KACD,EAAc,WAAW,KACzB,CAAC,KACC,kBAAC,GAAD;SAAc,WAAU;mBAAiD;SAE1D,CAAA;QAGlB,KACC,kBAAC,OAAD;SAAK,WAAU;mBACZ;SACG,CAAA;QAEI;;MAEb,CAAC,KACA,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,IAAD,EAAG,WAAU,UAAW,CAAA;;QACf;QAAQ;QACb;;MAEA;;IACK,CAAA,CACT;KAER"}
1
+ {"version":3,"file":"ProjectTagsWidget.js","names":[],"sources":["../../../src/components/tags/ProjectTagsWidget.tsx"],"sourcesContent":["/**\n * ProjectTagsWidget\n *\n * Local replacement for `@burdenoff/fe-libs/tag` `TagsWidget`, used until\n * fe-libs ships a fix for the non-clickable `AddTagTrigger` (the published\n * trigger swallows the props Radix Slot tries to forward via\n * `<PopoverTrigger asChild>`, so the popover never opens).\n *\n * This component bypasses the broken trigger by rendering a regular\n * `<PopoverTrigger>` (no `asChild`) so Radix mounts the click-handling\n * button itself. Internals (search, create, assign, remove) mirror the\n * fe-libs `TagsWidget` so the data path through wspace-tags-svc is\n * identical.\n *\n * TODO(fe-libs): once `@burdenoff/fe-libs/tag` `AddTagTrigger` forwards\n * props/ref to the underlying button, delete this file and switch back\n * to `<TagsWidget>` from `@burdenoff/fe-libs/tag`.\n */\n\nimport { gql, type ApolloClient } from '@apollo/client';\nimport { useLazyQuery, useMutation } from '@apollo/client/react';\nimport { Loader2, Plus, Tag as TagIcon, X } from 'lucide-react';\nimport { useCallback, useEffect, useRef, useState } from 'react';\n\nimport { type TagItem } from '@burdenoff/fe-libs/tag';\nimport { TagBadge } from '@burdenoff/fe-libs/tag';\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport { useGatewayClient } from '@burdenoff/fe-libs/shared/graphql';\nimport { cn } from '@burdenoff/fe-libs/shared/utils';\n\n// --- GraphQL ----------------------------------------------------------------\n\ninterface VocabResult {\n id: string;\n key: string;\n name: string;\n}\n\nconst SEARCH_TAGS = gql`\n query ProjectTagsWidget_SearchTags($query: String!, $vocabularyId: ID, $limit: Int) {\n searchTags(query: $query, vocabularyId: $vocabularyId, limit: $limit) {\n id\n key\n label\n value\n vocabularyId\n parentId\n hierarchyPath\n color\n icon\n description\n vocabulary {\n key\n }\n }\n }\n`;\n\nconst GET_VOCABULARY_BY_KEY = gql`\n query ProjectTagsWidget_GetVocabularyByKey($key: String!) {\n getVocabularyByKey(key: $key) {\n id\n key\n name\n }\n }\n`;\n\nconst CREATE_VOCABULARY = gql`\n mutation ProjectTagsWidget_CreateVocabulary($input: CreateVocabularyInput!) {\n createVocabulary(input: $input) {\n id\n key\n name\n }\n }\n`;\n\nconst CREATE_TAG = gql`\n mutation ProjectTagsWidget_CreateTag($input: CreateTagInput!) {\n createTag(input: $input) {\n id\n key\n label\n value\n vocabularyId\n parentId\n hierarchyPath\n color\n icon\n vocabulary {\n key\n }\n }\n }\n`;\n\nconst DEFAULT_VOCAB_KEY = 'general';\n\nfunction isTagItem(value: unknown): value is TagItem {\n if (!value || typeof value !== 'object') return false;\n const tag = value as Partial<Record<keyof TagItem, unknown>>;\n return typeof tag.id === 'string' && typeof tag.key === 'string' && typeof tag.label === 'string';\n}\n\n// --- Props ------------------------------------------------------------------\n\nexport interface ProjectTagsWidgetProps {\n /** Entity type recorded on the assigned-tag projection (e.g. \"Project\"). */\n entityType: string;\n /** Entity id (project id). */\n entityId: string;\n /** Workspace scope — widget renders nothing when empty. */\n workspaceId?: string;\n /** Controlled tag list owned by the parent. */\n tags: TagItem[];\n /** Persist a newly assigned tag on the entity. */\n onAdd: (tag: TagItem) => void | Promise<void>;\n /** Persist a tag removal on the entity. */\n onRemove: (tagId: string) => void | Promise<void>;\n /** Allow creating new tags on the fly. Default: true. */\n allowCreate?: boolean;\n /** Cap on assigned tags. Default 20. */\n maxTags?: number;\n className?: string;\n}\n\n// --- Component --------------------------------------------------------------\n\nexport function ProjectTagsWidget({\n entityType: _entityType,\n entityId,\n workspaceId,\n tags,\n onAdd,\n onRemove,\n allowCreate = true,\n maxTags = 20,\n className,\n}: ProjectTagsWidgetProps) {\n const [open, setOpen] = useState(false);\n const [search, setSearch] = useState('');\n const [createError, setCreateError] = useState<string | null>(null);\n const [adding, setAdding] = useState(false);\n const [resolvedVocabId, setResolvedVocabId] = useState<string | undefined>();\n const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n // Bun may install Apollo twice when fe-libs is consumed as a package; the runtime client is compatible.\n const wsClient = useGatewayClient('workspace') as unknown as ApolloClient;\n\n const skip = !workspaceId || !entityId;\n\n const assignedTagIds = new Set(tags.map((t) => t.id));\n const canAddMore = tags.length < maxTags;\n\n const [runSearch, { data: searchData, loading: searching }] = useLazyQuery<{\n searchTags: TagItem[];\n }>(SEARCH_TAGS, { client: wsClient });\n\n const searchResults = (searchData?.searchTags ?? []).filter(\n (tag): tag is TagItem => isTagItem(tag) && !assignedTagIds.has(tag.id)\n );\n\n const [createTag, { loading: creating }] = useMutation<{\n createTag: TagItem;\n }>(CREATE_TAG, { client: wsClient });\n\n const [fetchVocabByKey] = useLazyQuery<{\n getVocabularyByKey: VocabResult | null;\n }>(GET_VOCABULARY_BY_KEY, { client: wsClient });\n\n const [createVocabulary] = useMutation<{\n createVocabulary: VocabResult;\n }>(CREATE_VOCABULARY, { client: wsClient });\n\n const resolveVocabId = useCallback(async (): Promise<string> => {\n if (resolvedVocabId) return resolvedVocabId;\n\n const res = await fetchVocabByKey({ variables: { key: DEFAULT_VOCAB_KEY } });\n const existing = res.data?.getVocabularyByKey;\n if (existing) {\n setResolvedVocabId(existing.id);\n return existing.id;\n }\n\n const created = await createVocabulary({\n variables: {\n input: {\n key: DEFAULT_VOCAB_KEY,\n name: 'General',\n description: 'Default vocabulary for general-purpose tags',\n scope: 'WORKSPACE',\n allowCustomTags: true,\n },\n },\n });\n const newVocab = created.data?.createVocabulary;\n if (!newVocab) throw new Error('Failed to create default vocabulary');\n setResolvedVocabId(newVocab.id);\n return newVocab.id;\n }, [resolvedVocabId, fetchVocabByKey, createVocabulary]);\n\n const handleSearchChange = useCallback(\n (value: string) => {\n setSearch(value);\n if (createError) setCreateError(null);\n if (debounceRef.current) clearTimeout(debounceRef.current);\n if (!value.trim()) return;\n debounceRef.current = setTimeout(() => {\n void runSearch({\n variables: { query: value, vocabularyId: resolvedVocabId, limit: 10 },\n });\n }, 250);\n },\n [runSearch, resolvedVocabId, createError]\n );\n\n useEffect(() => {\n return () => {\n if (debounceRef.current) clearTimeout(debounceRef.current);\n };\n }, []);\n\n const handleAssign = useCallback(\n async (tag: TagItem) => {\n if (!canAddMore || adding) return;\n setAdding(true);\n try {\n const normalizedTag: TagItem = {\n ...tag,\n vocabularyKey: tag.vocabularyKey ?? tag.vocabulary?.key ?? null,\n };\n await onAdd(normalizedTag);\n setSearch('');\n setOpen(false);\n } finally {\n setAdding(false);\n }\n },\n [onAdd, canAddMore, adding]\n );\n\n const handleCreate = useCallback(async () => {\n if (!allowCreate || !search.trim() || creating) return;\n setCreateError(null);\n try {\n const label = search.trim();\n const key = label\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-|-$/g, '');\n if (!key) {\n setCreateError('Must contain at least one letter or number');\n return;\n }\n const vocabId = await resolveVocabId();\n const res = await createTag({\n variables: { input: { key, label, vocabularyId: vocabId } },\n });\n const created = res.data?.createTag;\n if (!isTagItem(created)) throw new Error('No data returned');\n await handleAssign(created);\n } catch (err) {\n setCreateError(err instanceof Error ? err.message : 'Failed to create tag');\n }\n }, [allowCreate, search, creating, resolveVocabId, createTag, handleAssign]);\n\n const handleRemove = useCallback(\n async (tagId: string) => {\n await onRemove(tagId);\n },\n [onRemove]\n );\n\n if (skip) return null;\n\n const showCreateOption = Boolean(\n allowCreate && search.trim() && !searching && searchResults.length === 0\n );\n\n return (\n <div className={cn('flex flex-wrap items-center gap-1.5', className)}>\n {tags.map((tag) => (\n <TagBadge\n key={tag.id}\n id={tag.id}\n label={tag.label}\n color={tag.color ?? undefined}\n icon={tag.icon ?? undefined}\n description={tag.description ?? undefined}\n removable\n onRemove={() => void handleRemove(tag.id)}\n size=\"sm\"\n />\n ))}\n\n {canAddMore && (\n <Popover open={open} onOpenChange={setOpen}>\n {/*\n Intentionally do NOT use `asChild` here — the fe-libs\n `AddTagTrigger` swallows the props Radix forwards, leaving the\n trigger non-clickable. Letting Radix render its own button\n restores the click handler.\n */}\n <PopoverTrigger\n type=\"button\"\n aria-label=\"Add tag\"\n className={cn(\n 'inline-flex items-center gap-1 px-2 py-0.5 rounded-full border border-dashed',\n 'border-border-subtle text-text-secondary text-xs',\n 'hover:border-foreground hover:text-text-primary transition-colors',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]'\n )}\n >\n <TagIcon className=\"size-3\" />\n <span>{tags.length > 0 ? 'Add' : 'Add tags'}</span>\n </PopoverTrigger>\n\n <PopoverContent\n className=\"z-50 w-72 max-w-[calc(100vw-2rem)] border-border-subtle/80 bg-popover p-0 shadow-xl\"\n align=\"start\"\n side=\"bottom\"\n sideOffset={8}\n >\n <Command shouldFilter={false}>\n <div className=\"relative\">\n <CommandInput\n placeholder=\"Search or create a tag...\"\n value={search}\n onValueChange={handleSearchChange}\n className=\"h-10 border-0 bg-transparent pr-9 text-sm outline-none placeholder:text-text-secondary\"\n />\n {searching && (\n <Loader2 className=\"absolute right-3 top-1/2 size-3.5 -translate-y-1/2 animate-spin text-text-secondary\" />\n )}\n </div>\n\n <CommandList className=\"max-h-48 overflow-y-auto\">\n {search.trim() === '' && (\n <CommandEmpty className=\"py-4 text-center text-xs text-text-secondary\">\n Type to search or create a tag\n </CommandEmpty>\n )}\n\n {searchResults.length > 0 && (\n <CommandGroup>\n {searchResults.map((tag) => (\n <CommandItem\n key={tag.id}\n value={tag.id}\n onSelect={() => void handleAssign(tag)}\n className=\"flex items-center gap-2 px-3 py-2 cursor-pointer\"\n disabled={adding}\n >\n {tag.icon && <span className=\"text-sm\">{tag.icon}</span>}\n <span\n className=\"truncate text-sm\"\n style={tag.color ? { color: tag.color } : undefined}\n >\n {tag.label}\n </span>\n {adding && <Loader2 className=\"ml-auto size-3 animate-spin\" />}\n </CommandItem>\n ))}\n </CommandGroup>\n )}\n\n {showCreateOption && (\n <CommandGroup>\n <CommandItem\n value={`__create__${search}`}\n onSelect={() => void handleCreate()}\n className=\"flex items-center gap-2 px-3 py-2 cursor-pointer text-primary\"\n disabled={creating}\n >\n {creating ? (\n <Loader2 className=\"size-3.5 animate-spin\" />\n ) : (\n <Plus className=\"size-3.5\" />\n )}\n <span className=\"text-sm truncate\">\n {creating ? 'Creating...' : `Create \"${search}\"`}\n </span>\n </CommandItem>\n </CommandGroup>\n )}\n\n {search.trim() !== '' &&\n !searching &&\n searchResults.length === 0 &&\n !showCreateOption && (\n <CommandEmpty className=\"py-4 text-center text-xs text-text-secondary\">\n No tags found\n </CommandEmpty>\n )}\n\n {createError && (\n <div className=\"px-3 py-2 text-xs text-status-error-text border-t border-border-subtle\">\n {createError}\n </div>\n )}\n </CommandList>\n\n {!canAddMore && (\n <div className=\"border-t border-border-subtle px-3 py-2 text-xs text-text-secondary flex items-center gap-1\">\n <X className=\"size-3\" />\n Maximum {maxTags} tags reached\n </div>\n )}\n </Command>\n </PopoverContent>\n </Popover>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;AAgDA,IAAM,IAAc,CAAG;;;;;;;;;;;;;;;;;;GAoBjB,IAAwB,CAAG;;;;;;;;GAU3B,KAAoB,CAAG;;;;;;;;GAUvB,KAAa,CAAG;;;;;;;;;;;;;;;;;GAmBhB,IAAoB;AAE1B,SAAS,EAAU,GAAkC;AACnD,KAAI,CAAC,KAAS,OAAO,KAAU,SAAU,QAAO;CAChD,IAAM,IAAM;AACZ,QAAO,OAAO,EAAI,MAAO,YAAY,OAAO,EAAI,OAAQ,YAAY,OAAO,EAAI,SAAU;;AA2B3F,SAAgB,EAAkB,EAChC,YAAY,GACZ,aACA,iBACA,SACA,UACA,aACA,iBAAc,IACd,aAAU,IACV,iBACyB;CACzB,IAAM,CAAC,IAAM,KAAW,EAAS,GAAM,EACjC,CAAC,GAAQ,KAAa,EAAS,GAAG,EAClC,CAAC,GAAa,KAAkB,EAAwB,KAAK,EAC7D,CAAC,GAAQ,KAAa,EAAS,GAAM,EACrC,CAAC,GAAiB,KAAsB,GAA8B,EACtE,IAAc,EAA6C,KAAK,EAGhE,IAAW,GAAiB,YAAY,EAExC,IAAO,CAAC,MAAe,CAAC,GAExB,IAAiB,IAAI,IAAI,EAAK,KAAK,MAAM,EAAE,GAAG,CAAC,EAC/C,IAAa,EAAK,SAAS,GAE3B,CAAC,GAAW,EAAE,MAAM,IAAY,SAAS,OAAe,EAE3D,GAAa,EAAE,QAAQ,GAAU,CAAC,EAE/B,KAAiB,IAAY,cAAc,EAAE,EAAE,QAClD,MAAwB,EAAU,EAAI,IAAI,CAAC,EAAe,IAAI,EAAI,GAAG,CACvE,EAEK,CAAC,GAAW,EAAE,SAAS,OAAc,EAExC,IAAY,EAAE,QAAQ,GAAU,CAAC,EAE9B,CAAC,KAAmB,EAEvB,GAAuB,EAAE,QAAQ,GAAU,CAAC,EAEzC,CAAC,KAAoB,EAExB,IAAmB,EAAE,QAAQ,GAAU,CAAC,EAErC,IAAiB,EAAY,YAA6B;AAC9D,MAAI,EAAiB,QAAO;EAG5B,IAAM,KADM,MAAM,EAAgB,EAAE,WAAW,EAAE,KAAK,GAAmB,EAAE,CAAC,EACvD,MAAM;AAC3B,MAAI,EAEF,QADA,EAAmB,EAAS,GAAG,EACxB,EAAS;EAclB,IAAM,KAXU,MAAM,EAAiB,EACrC,WAAW,EACT,OAAO;GACL,KAAK;GACL,MAAM;GACN,aAAa;GACb,OAAO;GACP,iBAAiB;GAClB,EACF,EACF,CAAC,EACuB,MAAM;AAC/B,MAAI,CAAC,EAAU,OAAU,MAAM,sCAAsC;AAErE,SADA,EAAmB,EAAS,GAAG,EACxB,EAAS;IACf;EAAC;EAAiB;EAAiB;EAAiB,CAAC,EAElD,KAAqB,GACxB,MAAkB;AACjB,IAAU,EAAM,EACZ,KAAa,EAAe,KAAK,EACjC,EAAY,WAAS,aAAa,EAAY,QAAQ,EACrD,EAAM,MAAM,KACjB,EAAY,UAAU,iBAAiB;AAChC,KAAU,EACb,WAAW;IAAE,OAAO;IAAO,cAAc;IAAiB,OAAO;IAAI,EACtE,CAAC;KACD,IAAI;IAET;EAAC;EAAW;EAAiB;EAAY,CAC1C;AAED,eACe;AACX,EAAI,EAAY,WAAS,aAAa,EAAY,QAAQ;IAE3D,EAAE,CAAC;CAEN,IAAM,IAAe,EACnB,OAAO,MAAiB;AAClB,SAAC,KAAc,IACnB;KAAU,GAAK;AACf,OAAI;AAOF,IAFA,MAAM,EAJyB;KAC7B,GAAG;KACH,eAAe,EAAI,iBAAiB,EAAI,YAAY,OAAO;KAC5D,CACyB,EAC1B,EAAU,GAAG,EACb,EAAQ,GAAM;aACN;AACR,MAAU,GAAM;;;IAGpB;EAAC;EAAO;EAAY;EAAO,CAC5B,EAEK,KAAe,EAAY,YAAY;AACvC,SAAC,KAAe,CAAC,EAAO,MAAM,IAAI,IACtC;KAAe,KAAK;AACpB,OAAI;IACF,IAAM,IAAQ,EAAO,MAAM,EACrB,IAAM,EACT,aAAa,CACb,QAAQ,eAAe,IAAI,CAC3B,QAAQ,UAAU,GAAG;AACxB,QAAI,CAAC,GAAK;AACR,OAAe,6CAA6C;AAC5D;;IAMF,IAAM,KAHM,MAAM,EAAU,EAC1B,WAAW,EAAE,OAAO;KAAE;KAAK;KAAO,cAFpB,MAAM,GAAgB;KAEqB,EAAE,EAC5D,CAAC,EACkB,MAAM;AAC1B,QAAI,CAAC,EAAU,EAAQ,CAAE,OAAU,MAAM,mBAAmB;AAC5D,UAAM,EAAa,EAAQ;YACpB,GAAK;AACZ,MAAe,aAAe,QAAQ,EAAI,UAAU,uBAAuB;;;IAE5E;EAAC;EAAa;EAAQ;EAAU;EAAgB;EAAW;EAAa,CAAC,EAEtE,KAAe,EACnB,OAAO,MAAkB;AACvB,QAAM,EAAS,EAAM;IAEvB,CAAC,EAAS,CACX;AAED,KAAI,EAAM,QAAO;CAEjB,IAAM,IAAmB,GACvB,KAAe,EAAO,MAAM,IAAI,CAAC,KAAa,EAAc,WAAW;AAGzE,QACE,kBAAC,OAAD;EAAK,WAAW,EAAG,uCAAuC,GAAU;YAApE,CACG,EAAK,KAAK,MACT,kBAAC,GAAD;GAEE,IAAI,EAAI;GACR,OAAO,EAAI;GACX,OAAO,EAAI,SAAS,KAAA;GACpB,MAAM,EAAI,QAAQ,KAAA;GAClB,aAAa,EAAI,eAAe,KAAA;GAChC,WAAA;GACA,gBAAgB,KAAK,GAAa,EAAI,GAAG;GACzC,MAAK;GACL,EATK,EAAI,GAST,CACF,EAED,KACC,kBAAC,GAAD;GAAe;GAAM,cAAc;aAAnC,CAOE,kBAAC,IAAD;IACE,MAAK;IACL,cAAW;IACX,WAAW,EACT,gFACA,oDACA,qEACA,+FACD;cARH,CAUE,kBAAC,GAAD,EAAS,WAAU,UAAW,CAAA,EAC9B,kBAAC,QAAD,EAAA,UAAO,EAAK,SAAS,IAAI,QAAQ,YAAkB,CAAA,CACpC;OAEjB,kBAAC,GAAD;IACE,WAAU;IACV,OAAM;IACN,MAAK;IACL,YAAY;cAEZ,kBAAC,IAAD;KAAS,cAAc;eAAvB;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QACE,aAAY;QACZ,OAAO;QACP,eAAe;QACf,WAAU;QACV,CAAA,EACD,KACC,kBAAC,GAAD,EAAS,WAAU,uFAAwF,CAAA,CAEzG;;MAEN,kBAAC,GAAD;OAAa,WAAU;iBAAvB;QACG,EAAO,MAAM,KAAK,MACjB,kBAAC,GAAD;SAAc,WAAU;mBAA+C;SAExD,CAAA;QAGhB,EAAc,SAAS,KACtB,kBAAC,GAAD,EAAA,UACG,EAAc,KAAK,MAClB,kBAAC,GAAD;SAEE,OAAO,EAAI;SACX,gBAAgB,KAAK,EAAa,EAAI;SACtC,WAAU;SACV,UAAU;mBALZ;UAOG,EAAI,QAAQ,kBAAC,QAAD;WAAM,WAAU;qBAAW,EAAI;WAAY,CAAA;UACxD,kBAAC,QAAD;WACE,WAAU;WACV,OAAO,EAAI,QAAQ,EAAE,OAAO,EAAI,OAAO,GAAG,KAAA;qBAEzC,EAAI;WACA,CAAA;UACN,KAAU,kBAAC,GAAD,EAAS,WAAU,+BAAgC,CAAA;UAClD;WAdP,EAAI,GAcG,CACd,EACW,CAAA;QAGhB,KACC,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;SACE,OAAO,aAAa;SACpB,gBAAgB,KAAK,IAAc;SACnC,WAAU;SACV,UAAU;mBAJZ,CAMG,IACC,kBAAC,GAAD,EAAS,WAAU,yBAA0B,CAAA,GAE7C,kBAAC,IAAD,EAAM,WAAU,YAAa,CAAA,EAE/B,kBAAC,QAAD;UAAM,WAAU;oBACb,IAAW,gBAAgB,WAAW,EAAO;UACzC,CAAA,CACK;YACD,CAAA;QAGhB,EAAO,MAAM,KAAK,MACjB,CAAC,KACD,EAAc,WAAW,KACzB,CAAC,KACC,kBAAC,GAAD;SAAc,WAAU;mBAA+C;SAExD,CAAA;QAGlB,KACC,kBAAC,OAAD;SAAK,WAAU;mBACZ;SACG,CAAA;QAEI;;MAEb,CAAC,KACA,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,IAAD,EAAG,WAAU,UAAW,CAAA;;QACf;QAAQ;QACb;;MAEA;;IACK,CAAA,CACT;KAER"}
@@ -25,22 +25,22 @@ function h() {
25
25
  className: "text-2xl sm:text-3xl font-bold",
26
26
  children: e(h, "activityList.title", "Activity")
27
27
  }), /* @__PURE__ */ o("p", {
28
- className: "text-muted-foreground",
28
+ className: "text-text-secondary",
29
29
  children: e(h, "activityList.description", "Track workspace activity")
30
30
  })] }), v ? /* @__PURE__ */ o(c, { children: /* @__PURE__ */ o(l, {
31
- className: "py-8 text-sm text-muted-foreground",
31
+ className: "py-8 text-sm text-text-secondary",
32
32
  children: e(h, "activityList.loading", "Loading activity…")
33
33
  }) }) : y ? /* @__PURE__ */ o(c, { children: /* @__PURE__ */ o(l, {
34
- className: "py-8 text-sm text-destructive",
34
+ className: "py-8 text-sm text-status-error-text",
35
35
  children: y instanceof Error ? y.message : e(h, "activityList.loadError", "Failed to load activity")
36
36
  }) }) : (_?.length ?? 0) === 0 ? /* @__PURE__ */ s("div", {
37
37
  className: "flex flex-col items-center justify-center py-12 text-center",
38
38
  children: [
39
39
  /* @__PURE__ */ o("div", {
40
- className: "rounded-full bg-muted p-6 mb-4",
40
+ className: "rounded-full bg-bg-sunken p-6 mb-4",
41
41
  children: /* @__PURE__ */ o(a, {
42
42
  size: 48,
43
- className: "text-muted-foreground"
43
+ className: "text-text-secondary"
44
44
  })
45
45
  }),
46
46
  /* @__PURE__ */ o("h3", {
@@ -48,7 +48,7 @@ function h() {
48
48
  children: e(h, "activityList.emptyTitle", "No recent activity")
49
49
  }),
50
50
  /* @__PURE__ */ o("p", {
51
- className: "text-muted-foreground max-w-sm",
51
+ className: "text-text-secondary max-w-sm",
52
52
  children: e(h, "activityList.emptyDescription", "Recent workspace actions will appear here as your team creates, updates, and manages work.")
53
53
  })
54
54
  ]
@@ -61,14 +61,14 @@ function h() {
61
61
  children: t.description || t.action
62
62
  })
63
63
  }), /* @__PURE__ */ s(l, {
64
- className: "space-y-1 text-sm text-muted-foreground",
64
+ className: "space-y-1 text-sm text-text-secondary",
65
65
  children: [
66
66
  /* @__PURE__ */ s("p", { children: [
67
67
  e(h, "activityList.labelAction", "Action"),
68
68
  ":",
69
69
  " ",
70
70
  /* @__PURE__ */ o("span", {
71
- className: "text-foreground",
71
+ className: "text-text-primary",
72
72
  children: t.action
73
73
  })
74
74
  ] }),
@@ -77,7 +77,7 @@ function h() {
77
77
  ":",
78
78
  " ",
79
79
  /* @__PURE__ */ o("span", {
80
- className: "text-foreground",
80
+ className: "text-text-primary",
81
81
  children: t.resourceType
82
82
  })
83
83
  ] }),
@@ -86,7 +86,7 @@ function h() {
86
86
  ":",
87
87
  " ",
88
88
  /* @__PURE__ */ o("span", {
89
- className: "text-foreground",
89
+ className: "text-text-primary",
90
90
  children: r(t.timestamp)
91
91
  })
92
92
  ] }),
@@ -95,7 +95,7 @@ function h() {
95
95
  ":",
96
96
  " ",
97
97
  /* @__PURE__ */ o("span", {
98
- className: "break-all text-foreground",
98
+ className: "break-all text-text-primary",
99
99
  children: t.userId
100
100
  })
101
101
  ] })
@@ -110,7 +110,7 @@ function h() {
110
110
  className: "text-lg font-semibold",
111
111
  children: e(h, "activityList.accessDenied", "Access Denied")
112
112
  }), /* @__PURE__ */ o("p", {
113
- className: "text-sm text-muted-foreground",
113
+ className: "text-sm text-text-secondary",
114
114
  children: e(h, "activityList.accessDeniedDescription", "You don't have permission to view activity.")
115
115
  })]
116
116
  })
@@ -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-muted-foreground\">\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-muted-foreground\">\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-muted-foreground\">\n {tWithFallback(t, 'activityList.loading', 'Loading activity…')}\n </CardContent>\n </Card>\n ) : error ? (\n <Card>\n <CardContent className=\"py-8 text-sm text-destructive\">\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-muted p-6 mb-4\">\n <Activity size={48} className=\"text-muted-foreground\" />\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-muted-foreground 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-muted-foreground\">\n <p>\n {tWithFallback(t, 'activityList.labelAction', 'Action')}:{' '}\n <span className=\"text-foreground\">{item.action}</span>\n </p>\n {item.resourceType && (\n <p>\n {tWithFallback(t, 'activityList.labelResource', 'Resource')}:{' '}\n <span className=\"text-foreground\">{item.resourceType}</span>\n </p>\n )}\n <p>\n {tWithFallback(t, 'activityList.labelWhen', 'When')}:{' '}\n <span className=\"text-foreground\">{formatDate(item.timestamp)}</span>\n </p>\n <p>\n {tWithFallback(t, 'activityList.labelActor', 'Actor')}:{' '}\n <span className=\"break-all text-foreground\">{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 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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"}
@@ -65,7 +65,7 @@ function E() {
65
65
  className: "p-6",
66
66
  children: [/* @__PURE__ */ d(v, {
67
67
  to: `${P}/list`,
68
- className: "inline-flex items-center gap-2 text-muted-foreground hover:text-foreground transition-colors mb-6",
68
+ className: "inline-flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors mb-6",
69
69
  children: [/* @__PURE__ */ u(l, { size: 20 }), t(E, "createWorkspace.backToWorkspaces", "Back to Workspaces")]
70
70
  }), /* @__PURE__ */ u(x, {
71
71
  variant: "card",
@@ -82,7 +82,7 @@ function E() {
82
82
  children: [
83
83
  /* @__PURE__ */ d(v, {
84
84
  to: `${P}/list`,
85
- className: "inline-flex items-center gap-2 text-muted-foreground hover:text-foreground transition-colors",
85
+ className: "inline-flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors",
86
86
  children: [/* @__PURE__ */ u(l, { size: 20 }), t(E, "createWorkspace.backToWorkspaces", "Back to Workspaces")]
87
87
  }),
88
88
  re && /* @__PURE__ */ u("div", {
@@ -99,7 +99,7 @@ function E() {
99
99
  className: "max-w-2xl mx-auto",
100
100
  children: [/* @__PURE__ */ d(h, { children: [/* @__PURE__ */ u(g, { children: t(E, "createWorkspace.title", "Create New Workspace") }), /* @__PURE__ */ u(ne, { children: t(E, "createWorkspace.subtitle", "Set up a new workspace for your team") })] }), /* @__PURE__ */ d(m, { children: [Z && /* @__PURE__ */ d("div", {
101
101
  role: "alert",
102
- className: "mb-4 flex items-start gap-2 rounded-md border border-warning/30 bg-warning/10 p-3 text-sm text-foreground",
102
+ className: "mb-4 flex items-start gap-2 rounded-md border border-warning/30 bg-warning/10 p-3 text-sm text-text-primary",
103
103
  children: [/* @__PURE__ */ u(c, {
104
104
  size: 16,
105
105
  className: "mt-0.5 shrink-0 text-warning"
@@ -113,21 +113,21 @@ function E() {
113
113
  children: [
114
114
  /* @__PURE__ */ u("label", {
115
115
  htmlFor: "workspace-name",
116
- className: "text-sm font-medium text-foreground",
116
+ className: "text-sm font-medium text-text-primary",
117
117
  children: t(E, "createWorkspace.nameLabel", "Workspace Name *")
118
118
  }),
119
119
  /* @__PURE__ */ u("input", {
120
120
  id: "workspace-name",
121
121
  placeholder: t(E, "createWorkspace.namePlaceholder", "My Workspace"),
122
- className: "h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground",
122
+ className: "h-10 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary",
123
123
  ...K.register("name")
124
124
  }),
125
125
  /* @__PURE__ */ u("p", {
126
- className: "text-xs text-muted-foreground",
126
+ className: "text-xs text-text-secondary",
127
127
  children: t(E, "createWorkspace.nameHint", "A descriptive name for your workspace")
128
128
  }),
129
129
  K.formState.errors.name?.message && /* @__PURE__ */ u("p", {
130
- className: "text-sm text-destructive",
130
+ className: "text-sm text-status-error-text",
131
131
  children: K.formState.errors.name.message
132
132
  })
133
133
  ]
@@ -137,21 +137,21 @@ function E() {
137
137
  children: [
138
138
  /* @__PURE__ */ u("label", {
139
139
  htmlFor: "workspace-slug",
140
- className: "text-sm font-medium text-foreground",
140
+ className: "text-sm font-medium text-text-primary",
141
141
  children: t(E, "createWorkspace.slugLabel", "Slug *")
142
142
  }),
143
143
  /* @__PURE__ */ u("input", {
144
144
  id: "workspace-slug",
145
145
  placeholder: t(E, "createWorkspace.slugPlaceholder", "my-workspace"),
146
- className: "h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground",
146
+ className: "h-10 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary",
147
147
  ...K.register("slug")
148
148
  }),
149
149
  /* @__PURE__ */ u("p", {
150
- className: "text-xs text-muted-foreground",
150
+ className: "text-xs text-text-secondary",
151
151
  children: t(E, "createWorkspace.slugHint", "URL-friendly identifier (auto-generated, but you can edit it)")
152
152
  }),
153
153
  K.formState.errors.slug?.message && /* @__PURE__ */ u("p", {
154
- className: "text-sm text-destructive",
154
+ className: "text-sm text-status-error-text",
155
155
  children: K.formState.errors.slug.message
156
156
  })
157
157
  ]
@@ -161,18 +161,18 @@ function E() {
161
161
  children: [
162
162
  /* @__PURE__ */ u("label", {
163
163
  htmlFor: "workspace-tenant",
164
- className: "text-sm font-medium text-foreground",
164
+ className: "text-sm font-medium text-text-primary",
165
165
  children: t(E, "createWorkspace.tenantLabel", "Tenant *")
166
166
  }),
167
167
  R ? /* @__PURE__ */ u(_, { className: "h-10 w-full" }) : z ? /* @__PURE__ */ d("div", {
168
- className: "flex items-center gap-2 text-sm text-destructive",
168
+ className: "flex items-center gap-2 text-sm text-status-error-text",
169
169
  children: [/* @__PURE__ */ u(c, { size: 16 }), t(E, "createWorkspace.failedToLoadTenants", "Failed to load tenants")]
170
170
  }) : U.length === 0 ? /* @__PURE__ */ u("div", {
171
- className: "text-sm text-muted-foreground",
171
+ className: "text-sm text-text-secondary",
172
172
  children: t(E, "createWorkspace.noTenantsAvailable", "No tenants available. Contact your administrator.")
173
173
  }) : /* @__PURE__ */ d("select", {
174
174
  id: "workspace-tenant",
175
- className: "h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground",
175
+ className: "h-10 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary",
176
176
  ...K.register("tenantId"),
177
177
  children: [/* @__PURE__ */ u("option", {
178
178
  value: "",
@@ -183,11 +183,11 @@ function E() {
183
183
  }, e.id))]
184
184
  }),
185
185
  /* @__PURE__ */ u("p", {
186
- className: "text-xs text-muted-foreground",
186
+ className: "text-xs text-text-secondary",
187
187
  children: t(E, "createWorkspace.tenantHint", "Select which tenant this workspace belongs to")
188
188
  }),
189
189
  K.formState.errors.tenantId?.message && /* @__PURE__ */ u("p", {
190
- className: "text-sm text-destructive",
190
+ className: "text-sm text-status-error-text",
191
191
  children: K.formState.errors.tenantId.message
192
192
  })
193
193
  ]
@@ -197,18 +197,18 @@ function E() {
197
197
  children: [
198
198
  /* @__PURE__ */ u("label", {
199
199
  htmlFor: "workspace-organization",
200
- className: "text-sm font-medium text-foreground",
200
+ className: "text-sm font-medium text-text-primary",
201
201
  children: t(E, "createWorkspace.organizationLabel", "Organization *")
202
202
  }),
203
203
  V || X ? /* @__PURE__ */ u(_, { className: "h-10 w-full" }) : H ? /* @__PURE__ */ d("div", {
204
- className: "flex items-center gap-2 text-sm text-destructive",
204
+ className: "flex items-center gap-2 text-sm text-status-error-text",
205
205
  children: [/* @__PURE__ */ u(c, { size: 16 }), t(E, "createWorkspace.failedToLoadOrganizations", "Failed to load organizations")]
206
206
  }) : q ? $.length === 0 ? /* @__PURE__ */ u("div", {
207
- className: "text-sm text-muted-foreground",
207
+ className: "text-sm text-text-secondary",
208
208
  children: t(E, "createWorkspace.noOrganizationsFound", "No organizations found for this tenant")
209
209
  }) : /* @__PURE__ */ d("select", {
210
210
  id: "workspace-organization",
211
- className: "h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground",
211
+ className: "h-10 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary",
212
212
  disabled: !q,
213
213
  ...K.register("organizationId"),
214
214
  children: [/* @__PURE__ */ u("option", {
@@ -219,15 +219,15 @@ function E() {
219
219
  children: e.name
220
220
  }, e.id))]
221
221
  }) : /* @__PURE__ */ u("div", {
222
- className: "text-sm text-muted-foreground",
222
+ className: "text-sm text-text-secondary",
223
223
  children: t(E, "createWorkspace.selectTenantFirst", "Please select a tenant first")
224
224
  }),
225
225
  /* @__PURE__ */ u("p", {
226
- className: "text-xs text-muted-foreground",
226
+ className: "text-xs text-text-secondary",
227
227
  children: t(E, "createWorkspace.organizationHint", "Select which organization owns this workspace")
228
228
  }),
229
229
  K.formState.errors.organizationId?.message && /* @__PURE__ */ u("p", {
230
- className: "text-sm text-destructive",
230
+ className: "text-sm text-status-error-text",
231
231
  children: K.formState.errors.organizationId.message
232
232
  })
233
233
  ]