@burdenoff/microfe-workspaces 2026.616.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 +99 -76
  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 +192 -181
  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 +2 -2
@@ -78,15 +78,15 @@ function C({ open: C, onOpenChange: w, workspaceId: T, projectId: E, projectName
78
78
  className: "space-y-4 overflow-hidden",
79
79
  children: [
80
80
  N ? /* @__PURE__ */ u("div", {
81
- className: "flex items-center gap-2 rounded-md border border-destructive/20 bg-destructive/10 p-3 text-sm text-destructive",
81
+ className: "flex items-center gap-2 rounded-md border border-status-error-border/20 bg-status-error-bg/10 p-3 text-sm text-status-error-text",
82
82
  children: [/* @__PURE__ */ l(o, { className: "size-4 shrink-0" }), /* @__PURE__ */ l("span", { children: N })]
83
83
  }) : null,
84
84
  G instanceof Error ? /* @__PURE__ */ u("div", {
85
- className: "flex items-center gap-2 rounded-md border border-destructive/20 bg-destructive/10 p-3 text-sm text-destructive",
85
+ className: "flex items-center gap-2 rounded-md border border-status-error-border/20 bg-status-error-bg/10 p-3 text-sm text-status-error-text",
86
86
  children: [/* @__PURE__ */ l(o, { className: "size-4 shrink-0" }), /* @__PURE__ */ l("span", { children: G.message })]
87
87
  }) : null,
88
88
  R instanceof Error ? /* @__PURE__ */ u("div", {
89
- className: "space-y-3 rounded-md border border-destructive/20 bg-destructive/10 p-3 text-sm text-destructive",
89
+ className: "space-y-3 rounded-md border border-status-error-border/20 bg-status-error-bg/10 p-3 text-sm text-status-error-text",
90
90
  children: [/* @__PURE__ */ u("div", {
91
91
  className: "flex items-center gap-2",
92
92
  children: [/* @__PURE__ */ l(o, { className: "size-4 shrink-0" }), /* @__PURE__ */ l("span", { children: R.message })]
@@ -100,7 +100,7 @@ function C({ open: C, onOpenChange: w, workspaceId: T, projectId: E, projectName
100
100
  className: "flex items-center gap-2",
101
101
  children: [/* @__PURE__ */ u("div", {
102
102
  className: "relative flex-1",
103
- children: [/* @__PURE__ */ l(re, { className: "absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" }), /* @__PURE__ */ l(f, {
103
+ children: [/* @__PURE__ */ l(re, { className: "absolute left-3 top-1/2 size-4 -translate-y-1/2 text-text-secondary" }), /* @__PURE__ */ l(f, {
104
104
  value: A,
105
105
  onChange: (e) => j(e.target.value),
106
106
  placeholder: "Search workspace members",
@@ -119,22 +119,22 @@ function C({ open: C, onOpenChange: w, workspaceId: T, projectId: E, projectName
119
119
  /* @__PURE__ */ l("div", {
120
120
  className: "max-h-[480px] space-y-3 overflow-y-auto pr-1",
121
121
  children: L ? null : I || ue || fe || J ? Array.from({ length: 5 }).map((e, t) => /* @__PURE__ */ u("div", {
122
- className: "rounded-lg border bg-card p-4",
122
+ className: "rounded-lg border bg-bg-surface p-4",
123
123
  children: [/* @__PURE__ */ l(_, { className: "h-5 w-40" }), /* @__PURE__ */ l(_, { className: "mt-2 h-4 w-56" })]
124
124
  }, t)) : G instanceof Error ? null : $.length === 0 ? /* @__PURE__ */ u("div", {
125
- className: "rounded-lg border bg-card p-8 text-center",
125
+ className: "rounded-lg border bg-bg-surface p-8 text-center",
126
126
  children: [
127
- /* @__PURE__ */ l(s, { className: "mx-auto mb-3 size-10 text-muted-foreground" }),
127
+ /* @__PURE__ */ l(s, { className: "mx-auto mb-3 size-10 text-text-secondary" }),
128
128
  /* @__PURE__ */ l("h3", {
129
129
  className: "text-lg font-semibold",
130
130
  children: "No eligible members found"
131
131
  }),
132
132
  /* @__PURE__ */ l("p", {
133
- className: "mt-2 text-sm text-muted-foreground",
133
+ className: "mt-2 text-sm text-text-secondary",
134
134
  children: "All workspace members are already part of this project, or nothing matched your search."
135
135
  }),
136
136
  Z ? /* @__PURE__ */ u("p", {
137
- className: "mt-3 text-xs text-muted-foreground",
137
+ className: "mt-3 text-xs text-text-secondary",
138
138
  children: [
139
139
  "Loaded the first ",
140
140
  x * b,
@@ -155,7 +155,7 @@ function C({ open: C, onOpenChange: w, workspaceId: T, projectId: E, projectName
155
155
  }) : /* @__PURE__ */ u(c, { children: [$.map((e) => {
156
156
  let t = Q[e.userId], n = S(e), r = k && le === e.userId;
157
157
  return /* @__PURE__ */ u("div", {
158
- className: "flex items-center justify-between gap-4 rounded-lg border bg-card p-4",
158
+ className: "flex items-center justify-between gap-4 rounded-lg border bg-bg-surface p-4",
159
159
  children: [/* @__PURE__ */ l("div", {
160
160
  className: "min-w-0 flex-1",
161
161
  children: t ? /* @__PURE__ */ l(v, {
@@ -167,10 +167,10 @@ function C({ open: C, onOpenChange: w, workspaceId: T, projectId: E, projectName
167
167
  className: "truncate font-medium",
168
168
  children: n.name
169
169
  }), n.email ? /* @__PURE__ */ l("p", {
170
- className: "truncate text-sm text-muted-foreground",
170
+ className: "truncate text-sm text-text-secondary",
171
171
  children: n.email
172
172
  }) : /* @__PURE__ */ l("p", {
173
- className: "truncate text-xs text-muted-foreground",
173
+ className: "truncate text-xs text-text-secondary",
174
174
  children: e.userId
175
175
  })]
176
176
  })
@@ -182,9 +182,9 @@ function C({ open: C, onOpenChange: w, workspaceId: T, projectId: E, projectName
182
182
  })]
183
183
  }, e.id);
184
184
  }), z ? /* @__PURE__ */ u("div", {
185
- className: "flex flex-col items-center gap-2 rounded-lg border border-dashed bg-muted/20 p-4 text-center",
185
+ className: "flex flex-col items-center gap-2 rounded-lg border border-dashed bg-bg-sunken/20 p-4 text-center",
186
186
  children: [/* @__PURE__ */ u("p", {
187
- className: "text-sm text-muted-foreground",
187
+ className: "text-sm text-text-secondary",
188
188
  children: [
189
189
  "Showing ",
190
190
  Y.length,
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectMemberPickerDialog.js","names":[],"sources":["../../src/components/ProjectMemberPickerDialog.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { AlertCircle, Search, UserPlus } from 'lucide-react';\nimport {\n ActorIdentity,\n useActorProfiles,\n type ActorInput,\n type ActorProfileMap,\n} from '@burdenoff/fe-libs/shared';\nimport {\n Badge,\n Button,\n Input,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n Skeleton,\n} from '@burdenoff/fe-libs/ui';\nimport { useAddProjectMember } from '../hooks/useProjectMemberMutations';\nimport { useInfiniteProjectMembers } from '../hooks/useProjectMembers';\nimport { useInfiniteWorkspaceMembers } from '../hooks/useWorkspaceMembers';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport { nativeImpact, nativeNotify } from '../utils/nativeBridge';\nimport type { WorkspaceMember } from '../types';\n\nconst MEMBER_PAGE_SIZE = 100;\nconst MAX_AUTOLOADED_PROJECT_MEMBER_PAGES = 10;\n\nfunction getFallbackDisplay(member: WorkspaceMember): { name: string; email?: string } {\n const meta = member.context?.metadata;\n const name =\n typeof meta?.displayName === 'string'\n ? meta.displayName\n : typeof meta?.name === 'string'\n ? meta.name\n : member.userId;\n const email = typeof meta?.email === 'string' ? meta.email : undefined;\n return { name, email };\n}\n\nexport interface ProjectMemberPickerDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n workspaceId: string;\n projectId: string;\n projectName?: string;\n}\n\nexport function ProjectMemberPickerDialog({\n open,\n onOpenChange,\n workspaceId,\n projectId,\n projectName,\n}: ProjectMemberPickerDialogProps) {\n const { apiGatewayUrl, authToken, organizationId } = useWorkspacesContext();\n const { mutate: addProjectMember, isPending: isAdding } = useAddProjectMember(workspaceId);\n const [searchValue, setSearchValue] = useState('');\n const [addingUserId, setAddingUserId] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n\n const {\n data: workspaceMembersPages,\n isLoading: isLoadingMembers,\n isError: isErrorWorkspaceMembers,\n error: workspaceMembersError,\n hasNextPage: hasMoreWorkspaceMembers,\n fetchNextPage: fetchNextWorkspaceMembersPage,\n isFetchingNextPage: isFetchingNextWorkspaceMembersPage,\n refetch: refetchWorkspaceMembers,\n } = useInfiniteWorkspaceMembers(workspaceId, MEMBER_PAGE_SIZE, open);\n\n const {\n data: projectMembersPages,\n isLoading: isLoadingProjectMembers,\n isError: isErrorProjectMembers,\n error: projectMembersError,\n hasNextPage: hasMoreProjectMembers,\n fetchNextPage: fetchNextProjectMembersPage,\n isFetchingNextPage: isFetchingNextProjectMembersPage,\n } = useInfiniteProjectMembers(projectId, workspaceId, MEMBER_PAGE_SIZE, open);\n\n useEffect(() => {\n if (\n !open ||\n !hasMoreProjectMembers ||\n isFetchingNextProjectMembersPage ||\n isErrorProjectMembers ||\n (projectMembersPages?.pages.length ?? 0) >= MAX_AUTOLOADED_PROJECT_MEMBER_PAGES\n ) {\n return;\n }\n\n void fetchNextProjectMembersPage();\n }, [\n fetchNextProjectMembersPage,\n hasMoreProjectMembers,\n isErrorProjectMembers,\n isFetchingNextProjectMembersPage,\n open,\n projectMembersPages?.pages.length,\n ]);\n\n const workspaceMembers = useMemo(\n () => workspaceMembersPages?.pages.flatMap((page) => page.items) ?? [],\n [workspaceMembersPages?.pages]\n );\n const existingUserIdSet = useMemo(\n () =>\n new Set(\n projectMembersPages?.pages.flatMap((page) => page.items.map((member) => member.userId)) ??\n []\n ),\n [projectMembersPages?.pages]\n );\n const totalWorkspaceMembers = workspaceMembersPages?.pages[0]?.total ?? workspaceMembers.length;\n const reachedProjectMemberAutoloadCap =\n (projectMembersPages?.pages.length ?? 0) >= MAX_AUTOLOADED_PROJECT_MEMBER_PAGES;\n const isAutoloadingProjectMembers =\n !isErrorProjectMembers && hasMoreProjectMembers && !reachedProjectMemberAutoloadCap;\n\n const actors = useMemo<ActorInput[]>(\n () => workspaceMembers.map((member) => ({ actorId: member.userId, actorType: 'user' })),\n [workspaceMembers]\n );\n const profileMap: ActorProfileMap = useActorProfiles(actors, {\n apiGatewayUrl,\n authToken,\n orgId: organizationId,\n workspaceId,\n });\n\n const eligibleMembers = useMemo(() => {\n const needle = searchValue.trim().toLowerCase();\n\n return workspaceMembers.filter((member) => {\n if (existingUserIdSet.has(member.userId)) {\n return false;\n }\n\n if (!needle) {\n return true;\n }\n\n const profile = profileMap[member.userId];\n if (profile) {\n if (profile.displayName.toLowerCase().includes(needle)) return true;\n if (profile.email?.toLowerCase().includes(needle)) return true;\n }\n\n const fallback = getFallbackDisplay(member);\n return (\n fallback.name.toLowerCase().includes(needle) ||\n fallback.email?.toLowerCase().includes(needle) ||\n member.userId.toLowerCase().includes(needle)\n );\n });\n }, [existingUserIdSet, profileMap, searchValue, workspaceMembers]);\n\n const handleAddMember = (userId: string) => {\n void nativeImpact('medium');\n setAddingUserId(userId);\n setError(null);\n\n addProjectMember(\n { projectId, userId },\n {\n onSuccess: () => {\n void nativeNotify('success');\n setAddingUserId(null);\n onOpenChange(false);\n },\n onError: (mutationError) => {\n void nativeNotify('error');\n setAddingUserId(null);\n setError(\n mutationError instanceof Error\n ? mutationError.message\n : 'Failed to add member to project'\n );\n },\n }\n );\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"z-[120] flex h-[min(85vh,760px)] flex-col overflow-hidden sm:max-w-[720px]\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Add member to project</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Add an existing workspace member to {projectName || 'this project'}. Only current\n workspace members are eligible.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4 overflow-hidden\">\n {error ? (\n <div className=\"flex items-center gap-2 rounded-md border border-destructive/20 bg-destructive/10 p-3 text-sm text-destructive\">\n <AlertCircle className=\"size-4 shrink-0\" />\n <span>{error}</span>\n </div>\n ) : null}\n\n {projectMembersError instanceof Error ? (\n <div className=\"flex items-center gap-2 rounded-md border border-destructive/20 bg-destructive/10 p-3 text-sm text-destructive\">\n <AlertCircle className=\"size-4 shrink-0\" />\n <span>{projectMembersError.message}</span>\n </div>\n ) : null}\n\n {workspaceMembersError instanceof Error ? (\n <div className=\"space-y-3 rounded-md border border-destructive/20 bg-destructive/10 p-3 text-sm text-destructive\">\n <div className=\"flex items-center gap-2\">\n <AlertCircle className=\"size-4 shrink-0\" />\n <span>{workspaceMembersError.message}</span>\n </div>\n <Button variant=\"outline\" onClick={() => void refetchWorkspaceMembers()}>\n Retry Loading Members\n </Button>\n </div>\n ) : null}\n\n <div className=\"flex items-center gap-2\">\n <div className=\"relative flex-1\">\n <Search className=\"absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground\" />\n <Input\n value={searchValue}\n onChange={(event) => setSearchValue(event.target.value)}\n placeholder=\"Search workspace members\"\n className=\"pl-9\"\n disabled={isAdding}\n />\n </div>\n <Badge variant=\"secondary\">\n {hasMoreWorkspaceMembers ? `${eligibleMembers.length}+` : eligibleMembers.length}{' '}\n eligible\n </Badge>\n </div>\n\n <div className=\"max-h-[480px] space-y-3 overflow-y-auto pr-1\">\n {isErrorWorkspaceMembers ? null : isLoadingMembers ||\n isLoadingProjectMembers ||\n isAutoloadingProjectMembers ||\n isFetchingNextProjectMembersPage ? (\n Array.from({ length: 5 }).map((_, index) => (\n <div key={index} className=\"rounded-lg border bg-card p-4\">\n <Skeleton className=\"h-5 w-40\" />\n <Skeleton className=\"mt-2 h-4 w-56\" />\n </div>\n ))\n ) : projectMembersError instanceof Error ? null : eligibleMembers.length === 0 ? (\n <div className=\"rounded-lg border bg-card p-8 text-center\">\n <UserPlus className=\"mx-auto mb-3 size-10 text-muted-foreground\" />\n <h3 className=\"text-lg font-semibold\">No eligible members found</h3>\n <p className=\"mt-2 text-sm text-muted-foreground\">\n All workspace members are already part of this project, or nothing matched your\n search.\n </p>\n {reachedProjectMemberAutoloadCap ? (\n <p className=\"mt-3 text-xs text-muted-foreground\">\n Loaded the first {MAX_AUTOLOADED_PROJECT_MEMBER_PAGES * MEMBER_PAGE_SIZE}{' '}\n project members to avoid an excessively long blocking load.\n </p>\n ) : null}\n {hasMoreWorkspaceMembers ? (\n <div className=\"mt-4\">\n <Button\n variant=\"outline\"\n onClick={() => void fetchNextWorkspaceMembersPage()}\n disabled={isFetchingNextWorkspaceMembersPage || isAdding}\n >\n {isFetchingNextWorkspaceMembersPage ? 'Loading more…' : 'Load More Members'}\n </Button>\n </div>\n ) : null}\n </div>\n ) : (\n <>\n {eligibleMembers.map((member) => {\n const profile = profileMap[member.userId];\n const fallback = getFallbackDisplay(member);\n const isSubmitting = isAdding && addingUserId === member.userId;\n\n return (\n <div\n key={member.id}\n className=\"flex items-center justify-between gap-4 rounded-lg border bg-card p-4\"\n >\n <div className=\"min-w-0 flex-1\">\n {profile ? (\n <ActorIdentity profile={profile} variant=\"cell\" />\n ) : (\n <div className=\"min-w-0\">\n <p className=\"truncate font-medium\">{fallback.name}</p>\n {fallback.email ? (\n <p className=\"truncate text-sm text-muted-foreground\">\n {fallback.email}\n </p>\n ) : (\n <p className=\"truncate text-xs text-muted-foreground\">\n {member.userId}\n </p>\n )}\n </div>\n )}\n </div>\n\n <Button\n size=\"sm\"\n onClick={() => handleAddMember(member.userId)}\n disabled={isAdding}\n >\n <UserPlus className=\"mr-2 size-4\" />\n {isSubmitting ? 'Adding...' : 'Add'}\n </Button>\n </div>\n );\n })}\n\n {hasMoreWorkspaceMembers ? (\n <div className=\"flex flex-col items-center gap-2 rounded-lg border border-dashed bg-muted/20 p-4 text-center\">\n <p className=\"text-sm text-muted-foreground\">\n Showing {workspaceMembers.length} of {totalWorkspaceMembers} workspace\n members.\n </p>\n <Button\n variant=\"outline\"\n onClick={() => void fetchNextWorkspaceMembersPage()}\n disabled={isFetchingNextWorkspaceMembersPage || isAdding}\n >\n {isFetchingNextWorkspaceMembersPage ? 'Loading more…' : 'Load More Members'}\n </Button>\n </div>\n ) : null}\n </>\n )}\n </div>\n </div>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":";;;;;;;;;;;AA0BA,IAAM,IAAmB,KACnB,IAAsC;AAE5C,SAAS,EAAmB,GAA2D;CACrF,IAAM,IAAO,EAAO,SAAS;AAQ7B,QAAO;EAAE,MANP,OAAO,GAAM,eAAgB,WACzB,EAAK,cACL,OAAO,GAAM,QAAS,WACpB,EAAK,OACL,EAAO;EAEA,OADD,OAAO,GAAM,SAAU,WAAW,EAAK,QAAQ,KAAA;EACvC;;AAWxB,SAAgB,EAA0B,EACxC,SACA,iBACA,gBACA,cACA,kBACiC;CACjC,IAAM,EAAE,kBAAe,eAAW,uBAAmB,GAAsB,EACrE,EAAE,QAAQ,IAAkB,WAAW,MAAa,GAAoB,EAAY,EACpF,CAAC,GAAa,KAAkB,EAAS,GAAG,EAC5C,CAAC,IAAc,KAAmB,EAAwB,KAAK,EAC/D,CAAC,GAAO,KAAY,EAAwB,KAAK,EAEjD,EACJ,MAAM,GACN,WAAW,GACX,SAAS,GACT,OAAO,GACP,aAAa,GACb,eAAe,GACf,oBAAoB,GACpB,SAAS,MACP,EAA4B,GAAa,GAAkB,EAAK,EAE9D,EACJ,MAAM,GACN,WAAW,IACX,SAAS,GACT,OAAO,GACP,aAAa,GACb,eAAe,GACf,oBAAoB,MAClB,GAA0B,GAAW,GAAa,GAAkB,EAAK;AAE7E,UAAgB;AAEZ,GAAC,KACD,CAAC,KACD,KACA,MACC,GAAqB,MAAM,UAAU,MAAM,KAKzC,GAA6B;IACjC;EACD;EACA;EACA;EACA;EACA;EACA,GAAqB,MAAM;EAC5B,CAAC;CAEF,IAAM,IAAmB,QACjB,GAAuB,MAAM,SAAS,MAAS,EAAK,MAAM,IAAI,EAAE,EACtE,CAAC,GAAuB,MAAM,CAC/B,EACK,IAAoB,QAEtB,IAAI,IACF,GAAqB,MAAM,SAAS,MAAS,EAAK,MAAM,KAAK,MAAW,EAAO,OAAO,CAAC,IACrF,EAAE,CACL,EACH,CAAC,GAAqB,MAAM,CAC7B,EACK,KAAwB,GAAuB,MAAM,IAAI,SAAS,EAAiB,QACnF,KACH,GAAqB,MAAM,UAAU,MAAM,GACxC,KACJ,CAAC,KAAyB,KAAyB,CAAC,GAMhD,IAA8B,EAJrB,QACP,EAAiB,KAAK,OAAY;EAAE,SAAS,EAAO;EAAQ,WAAW;EAAQ,EAAE,EACvF,CAAC,EAAiB,CACnB,EAC4D;EAC3D;EACA;EACA,OAAO;EACP;EACD,CAAC,EAEI,IAAkB,QAAc;EACpC,IAAM,IAAS,EAAY,MAAM,CAAC,aAAa;AAE/C,SAAO,EAAiB,QAAQ,MAAW;AACzC,OAAI,EAAkB,IAAI,EAAO,OAAO,CACtC,QAAO;AAGT,OAAI,CAAC,EACH,QAAO;GAGT,IAAM,IAAU,EAAW,EAAO;AAClC,OAAI,MACE,EAAQ,YAAY,aAAa,CAAC,SAAS,EAAO,IAClD,EAAQ,OAAO,aAAa,CAAC,SAAS,EAAO,EAAE,QAAO;GAG5D,IAAM,IAAW,EAAmB,EAAO;AAC3C,UACE,EAAS,KAAK,aAAa,CAAC,SAAS,EAAO,IAC5C,EAAS,OAAO,aAAa,CAAC,SAAS,EAAO,IAC9C,EAAO,OAAO,aAAa,CAAC,SAAS,EAAO;IAE9C;IACD;EAAC;EAAmB;EAAY;EAAa;EAAiB,CAAC,EAE5D,MAAmB,MAAmB;AAK1C,EAJK,EAAa,SAAS,EAC3B,EAAgB,EAAO,EACvB,EAAS,KAAK,EAEd,GACE;GAAE;GAAW;GAAQ,EACrB;GACE,iBAAiB;AAGf,IAFK,EAAa,UAAU,EAC5B,EAAgB,KAAK,EACrB,EAAa,GAAM;;GAErB,UAAU,MAAkB;AAG1B,IAFK,EAAa,QAAQ,EAC1B,EAAgB,KAAK,EACrB,EACE,aAAyB,QACrB,EAAc,UACd,kCACL;;GAEJ,CACF;;AAGH,QACE,kBAAC,GAAD;EAAwB;EAAoB;YAC1C,kBAAC,IAAD;GAAyB,WAAU;aAAnC,CACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAuB,yBAA6C,CAAA,EACpE,kBAAC,GAAD,EAAA,UAAA;IAA6B;IACU,KAAe;IAAe;IAEvC,EAAA,CAAA,CACP,EAAA,CAAA,EAEzB,kBAAC,OAAD;IAAK,WAAU;cAAf;KACG,IACC,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD,EAAa,WAAU,mBAAoB,CAAA,EAC3C,kBAAC,QAAD,EAAA,UAAO,GAAa,CAAA,CAChB;UACJ;KAEH,aAA+B,QAC9B,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD,EAAa,WAAU,mBAAoB,CAAA,EAC3C,kBAAC,QAAD,EAAA,UAAO,EAAoB,SAAe,CAAA,CACtC;UACJ;KAEH,aAAiC,QAChC,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAa,WAAU,mBAAoB,CAAA,EAC3C,kBAAC,QAAD,EAAA,UAAO,EAAsB,SAAe,CAAA,CACxC;UACN,kBAAC,GAAD;OAAQ,SAAQ;OAAU,eAAe,KAAK,GAAyB;iBAAE;OAEhE,CAAA,CACL;UACJ;KAEJ,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,IAAD,EAAQ,WAAU,yEAA0E,CAAA,EAC5F,kBAAC,GAAD;QACE,OAAO;QACP,WAAW,MAAU,EAAe,EAAM,OAAO,MAAM;QACvD,aAAY;QACZ,WAAU;QACV,UAAU;QACV,CAAA,CACE;UACN,kBAAC,IAAD;OAAO,SAAQ;iBAAf;QACG,IAA0B,GAAG,EAAgB,OAAO,KAAK,EAAgB;QAAQ;QAAI;QAEhF;SACJ;;KAEN,kBAAC,OAAD;MAAK,WAAU;gBACZ,IAA0B,OAAO,KAChC,MACA,MACA,IACA,MAAM,KAAK,EAAE,QAAQ,GAAG,CAAC,CAAC,KAAK,GAAG,MAChC,kBAAC,OAAD;OAAiB,WAAU;iBAA3B,CACE,kBAAC,GAAD,EAAU,WAAU,YAAa,CAAA,EACjC,kBAAC,GAAD,EAAU,WAAU,iBAAkB,CAAA,CAClC;SAHI,EAGJ,CACN,GACA,aAA+B,QAAQ,OAAO,EAAgB,WAAW,IAC3E,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,GAAD,EAAU,WAAU,8CAA+C,CAAA;QACnE,kBAAC,MAAD;SAAI,WAAU;mBAAwB;SAA8B,CAAA;QACpE,kBAAC,KAAD;SAAG,WAAU;mBAAqC;SAG9C,CAAA;QACH,IACC,kBAAC,KAAD;SAAG,WAAU;mBAAb;UAAkD;UAC9B,IAAsC;UAAkB;UAAI;UAE5E;aACF;QACH,IACC,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,GAAD;UACE,SAAQ;UACR,eAAe,KAAK,GAA+B;UACnD,UAAU,KAAsC;oBAE/C,IAAqC,kBAAkB;UACjD,CAAA;SACL,CAAA,GACJ;QACA;WAEN,kBAAA,GAAA,EAAA,UAAA,CACG,EAAgB,KAAK,MAAW;OAC/B,IAAM,IAAU,EAAW,EAAO,SAC5B,IAAW,EAAmB,EAAO,EACrC,IAAe,KAAY,OAAiB,EAAO;AAEzD,cACE,kBAAC,OAAD;QAEE,WAAU;kBAFZ,CAIE,kBAAC,OAAD;SAAK,WAAU;mBACZ,IACC,kBAAC,GAAD;UAAwB;UAAS,SAAQ;UAAS,CAAA,GAElD,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,KAAD;WAAG,WAAU;qBAAwB,EAAS;WAAS,CAAA,EACtD,EAAS,QACR,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAS;WACR,CAAA,GAEJ,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAO;WACN,CAAA,CAEF;;SAEJ,CAAA,EAEN,kBAAC,GAAD;SACE,MAAK;SACL,eAAe,GAAgB,EAAO,OAAO;SAC7C,UAAU;mBAHZ,CAKE,kBAAC,GAAD,EAAU,WAAU,eAAgB,CAAA,EACnC,IAAe,cAAc,MACvB;WACL;UA9BC,EAAO,GA8BR;QAER,EAED,IACC,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBAAb;SAA6C;SAClC,EAAiB;SAAO;SAAK;SAAsB;SAE1D;WACJ,kBAAC,GAAD;QACE,SAAQ;QACR,eAAe,KAAK,GAA+B;QACnD,UAAU,KAAsC;kBAE/C,IAAqC,kBAAkB;QACjD,CAAA,CACL;WACJ,KACH,EAAA,CAAA;MAED,CAAA;KACF;MACkB;;EACT,CAAA"}
1
+ {"version":3,"file":"ProjectMemberPickerDialog.js","names":[],"sources":["../../src/components/ProjectMemberPickerDialog.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { AlertCircle, Search, UserPlus } from 'lucide-react';\nimport {\n ActorIdentity,\n useActorProfiles,\n type ActorInput,\n type ActorProfileMap,\n} from '@burdenoff/fe-libs/shared';\nimport {\n Badge,\n Button,\n Input,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n Skeleton,\n} from '@burdenoff/fe-libs/ui';\nimport { useAddProjectMember } from '../hooks/useProjectMemberMutations';\nimport { useInfiniteProjectMembers } from '../hooks/useProjectMembers';\nimport { useInfiniteWorkspaceMembers } from '../hooks/useWorkspaceMembers';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport { nativeImpact, nativeNotify } from '../utils/nativeBridge';\nimport type { WorkspaceMember } from '../types';\n\nconst MEMBER_PAGE_SIZE = 100;\nconst MAX_AUTOLOADED_PROJECT_MEMBER_PAGES = 10;\n\nfunction getFallbackDisplay(member: WorkspaceMember): { name: string; email?: string } {\n const meta = member.context?.metadata;\n const name =\n typeof meta?.displayName === 'string'\n ? meta.displayName\n : typeof meta?.name === 'string'\n ? meta.name\n : member.userId;\n const email = typeof meta?.email === 'string' ? meta.email : undefined;\n return { name, email };\n}\n\nexport interface ProjectMemberPickerDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n workspaceId: string;\n projectId: string;\n projectName?: string;\n}\n\nexport function ProjectMemberPickerDialog({\n open,\n onOpenChange,\n workspaceId,\n projectId,\n projectName,\n}: ProjectMemberPickerDialogProps) {\n const { apiGatewayUrl, authToken, organizationId } = useWorkspacesContext();\n const { mutate: addProjectMember, isPending: isAdding } = useAddProjectMember(workspaceId);\n const [searchValue, setSearchValue] = useState('');\n const [addingUserId, setAddingUserId] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n\n const {\n data: workspaceMembersPages,\n isLoading: isLoadingMembers,\n isError: isErrorWorkspaceMembers,\n error: workspaceMembersError,\n hasNextPage: hasMoreWorkspaceMembers,\n fetchNextPage: fetchNextWorkspaceMembersPage,\n isFetchingNextPage: isFetchingNextWorkspaceMembersPage,\n refetch: refetchWorkspaceMembers,\n } = useInfiniteWorkspaceMembers(workspaceId, MEMBER_PAGE_SIZE, open);\n\n const {\n data: projectMembersPages,\n isLoading: isLoadingProjectMembers,\n isError: isErrorProjectMembers,\n error: projectMembersError,\n hasNextPage: hasMoreProjectMembers,\n fetchNextPage: fetchNextProjectMembersPage,\n isFetchingNextPage: isFetchingNextProjectMembersPage,\n } = useInfiniteProjectMembers(projectId, workspaceId, MEMBER_PAGE_SIZE, open);\n\n useEffect(() => {\n if (\n !open ||\n !hasMoreProjectMembers ||\n isFetchingNextProjectMembersPage ||\n isErrorProjectMembers ||\n (projectMembersPages?.pages.length ?? 0) >= MAX_AUTOLOADED_PROJECT_MEMBER_PAGES\n ) {\n return;\n }\n\n void fetchNextProjectMembersPage();\n }, [\n fetchNextProjectMembersPage,\n hasMoreProjectMembers,\n isErrorProjectMembers,\n isFetchingNextProjectMembersPage,\n open,\n projectMembersPages?.pages.length,\n ]);\n\n const workspaceMembers = useMemo(\n () => workspaceMembersPages?.pages.flatMap((page) => page.items) ?? [],\n [workspaceMembersPages?.pages]\n );\n const existingUserIdSet = useMemo(\n () =>\n new Set(\n projectMembersPages?.pages.flatMap((page) => page.items.map((member) => member.userId)) ??\n []\n ),\n [projectMembersPages?.pages]\n );\n const totalWorkspaceMembers = workspaceMembersPages?.pages[0]?.total ?? workspaceMembers.length;\n const reachedProjectMemberAutoloadCap =\n (projectMembersPages?.pages.length ?? 0) >= MAX_AUTOLOADED_PROJECT_MEMBER_PAGES;\n const isAutoloadingProjectMembers =\n !isErrorProjectMembers && hasMoreProjectMembers && !reachedProjectMemberAutoloadCap;\n\n const actors = useMemo<ActorInput[]>(\n () => workspaceMembers.map((member) => ({ actorId: member.userId, actorType: 'user' })),\n [workspaceMembers]\n );\n const profileMap: ActorProfileMap = useActorProfiles(actors, {\n apiGatewayUrl,\n authToken,\n orgId: organizationId,\n workspaceId,\n });\n\n const eligibleMembers = useMemo(() => {\n const needle = searchValue.trim().toLowerCase();\n\n return workspaceMembers.filter((member) => {\n if (existingUserIdSet.has(member.userId)) {\n return false;\n }\n\n if (!needle) {\n return true;\n }\n\n const profile = profileMap[member.userId];\n if (profile) {\n if (profile.displayName.toLowerCase().includes(needle)) return true;\n if (profile.email?.toLowerCase().includes(needle)) return true;\n }\n\n const fallback = getFallbackDisplay(member);\n return (\n fallback.name.toLowerCase().includes(needle) ||\n fallback.email?.toLowerCase().includes(needle) ||\n member.userId.toLowerCase().includes(needle)\n );\n });\n }, [existingUserIdSet, profileMap, searchValue, workspaceMembers]);\n\n const handleAddMember = (userId: string) => {\n void nativeImpact('medium');\n setAddingUserId(userId);\n setError(null);\n\n addProjectMember(\n { projectId, userId },\n {\n onSuccess: () => {\n void nativeNotify('success');\n setAddingUserId(null);\n onOpenChange(false);\n },\n onError: (mutationError) => {\n void nativeNotify('error');\n setAddingUserId(null);\n setError(\n mutationError instanceof Error\n ? mutationError.message\n : 'Failed to add member to project'\n );\n },\n }\n );\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"z-[120] flex h-[min(85vh,760px)] flex-col overflow-hidden sm:max-w-[720px]\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Add member to project</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Add an existing workspace member to {projectName || 'this project'}. Only current\n workspace members are eligible.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4 overflow-hidden\">\n {error ? (\n <div className=\"flex items-center gap-2 rounded-md border border-status-error-border/20 bg-status-error-bg/10 p-3 text-sm text-status-error-text\">\n <AlertCircle className=\"size-4 shrink-0\" />\n <span>{error}</span>\n </div>\n ) : null}\n\n {projectMembersError instanceof Error ? (\n <div className=\"flex items-center gap-2 rounded-md border border-status-error-border/20 bg-status-error-bg/10 p-3 text-sm text-status-error-text\">\n <AlertCircle className=\"size-4 shrink-0\" />\n <span>{projectMembersError.message}</span>\n </div>\n ) : null}\n\n {workspaceMembersError instanceof Error ? (\n <div className=\"space-y-3 rounded-md border border-status-error-border/20 bg-status-error-bg/10 p-3 text-sm text-status-error-text\">\n <div className=\"flex items-center gap-2\">\n <AlertCircle className=\"size-4 shrink-0\" />\n <span>{workspaceMembersError.message}</span>\n </div>\n <Button variant=\"outline\" onClick={() => void refetchWorkspaceMembers()}>\n Retry Loading Members\n </Button>\n </div>\n ) : null}\n\n <div className=\"flex items-center gap-2\">\n <div className=\"relative flex-1\">\n <Search className=\"absolute left-3 top-1/2 size-4 -translate-y-1/2 text-text-secondary\" />\n <Input\n value={searchValue}\n onChange={(event) => setSearchValue(event.target.value)}\n placeholder=\"Search workspace members\"\n className=\"pl-9\"\n disabled={isAdding}\n />\n </div>\n <Badge variant=\"secondary\">\n {hasMoreWorkspaceMembers ? `${eligibleMembers.length}+` : eligibleMembers.length}{' '}\n eligible\n </Badge>\n </div>\n\n <div className=\"max-h-[480px] space-y-3 overflow-y-auto pr-1\">\n {isErrorWorkspaceMembers ? null : isLoadingMembers ||\n isLoadingProjectMembers ||\n isAutoloadingProjectMembers ||\n isFetchingNextProjectMembersPage ? (\n Array.from({ length: 5 }).map((_, index) => (\n <div key={index} className=\"rounded-lg border bg-bg-surface p-4\">\n <Skeleton className=\"h-5 w-40\" />\n <Skeleton className=\"mt-2 h-4 w-56\" />\n </div>\n ))\n ) : projectMembersError instanceof Error ? null : eligibleMembers.length === 0 ? (\n <div className=\"rounded-lg border bg-bg-surface p-8 text-center\">\n <UserPlus className=\"mx-auto mb-3 size-10 text-text-secondary\" />\n <h3 className=\"text-lg font-semibold\">No eligible members found</h3>\n <p className=\"mt-2 text-sm text-text-secondary\">\n All workspace members are already part of this project, or nothing matched your\n search.\n </p>\n {reachedProjectMemberAutoloadCap ? (\n <p className=\"mt-3 text-xs text-text-secondary\">\n Loaded the first {MAX_AUTOLOADED_PROJECT_MEMBER_PAGES * MEMBER_PAGE_SIZE}{' '}\n project members to avoid an excessively long blocking load.\n </p>\n ) : null}\n {hasMoreWorkspaceMembers ? (\n <div className=\"mt-4\">\n <Button\n variant=\"outline\"\n onClick={() => void fetchNextWorkspaceMembersPage()}\n disabled={isFetchingNextWorkspaceMembersPage || isAdding}\n >\n {isFetchingNextWorkspaceMembersPage ? 'Loading more…' : 'Load More Members'}\n </Button>\n </div>\n ) : null}\n </div>\n ) : (\n <>\n {eligibleMembers.map((member) => {\n const profile = profileMap[member.userId];\n const fallback = getFallbackDisplay(member);\n const isSubmitting = isAdding && addingUserId === member.userId;\n\n return (\n <div\n key={member.id}\n className=\"flex items-center justify-between gap-4 rounded-lg border bg-bg-surface p-4\"\n >\n <div className=\"min-w-0 flex-1\">\n {profile ? (\n <ActorIdentity profile={profile} variant=\"cell\" />\n ) : (\n <div className=\"min-w-0\">\n <p className=\"truncate font-medium\">{fallback.name}</p>\n {fallback.email ? (\n <p className=\"truncate text-sm text-text-secondary\">\n {fallback.email}\n </p>\n ) : (\n <p className=\"truncate text-xs text-text-secondary\">\n {member.userId}\n </p>\n )}\n </div>\n )}\n </div>\n\n <Button\n size=\"sm\"\n onClick={() => handleAddMember(member.userId)}\n disabled={isAdding}\n >\n <UserPlus className=\"mr-2 size-4\" />\n {isSubmitting ? 'Adding...' : 'Add'}\n </Button>\n </div>\n );\n })}\n\n {hasMoreWorkspaceMembers ? (\n <div className=\"flex flex-col items-center gap-2 rounded-lg border border-dashed bg-bg-sunken/20 p-4 text-center\">\n <p className=\"text-sm text-text-secondary\">\n Showing {workspaceMembers.length} of {totalWorkspaceMembers} workspace\n members.\n </p>\n <Button\n variant=\"outline\"\n onClick={() => void fetchNextWorkspaceMembersPage()}\n disabled={isFetchingNextWorkspaceMembersPage || isAdding}\n >\n {isFetchingNextWorkspaceMembersPage ? 'Loading more…' : 'Load More Members'}\n </Button>\n </div>\n ) : null}\n </>\n )}\n </div>\n </div>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
@@ -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":"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"}
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":"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"}
@@ -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
  })