@burdenoff/microfe-workspaces 2026.623.1 → 2026.624.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/dist/components/CreateWorkspaceDialog.js +5 -5
  2. package/dist/components/CreateWorkspaceDialog.js.map +1 -1
  3. package/dist/components/InviteMemberDialog.js +11 -11
  4. package/dist/components/InviteMemberDialog.js.map +1 -1
  5. package/dist/components/ProjectCard.js +5 -5
  6. package/dist/components/ProjectCard.js.map +1 -1
  7. package/dist/components/ProjectMemberPickerDialog.js +14 -14
  8. package/dist/components/ProjectMemberPickerDialog.js.map +1 -1
  9. package/dist/components/WorkspaceCard.js +3 -3
  10. package/dist/components/WorkspaceCard.js.map +1 -1
  11. package/dist/components/WorkspacesErrorBoundary.js +9 -9
  12. package/dist/components/WorkspacesErrorBoundary.js.map +1 -1
  13. package/dist/components/tags/ProjectTagsWidget.js +8 -8
  14. package/dist/components/tags/ProjectTagsWidget.js.map +1 -1
  15. package/dist/pages/ActivityListPage.js +12 -12
  16. package/dist/pages/ActivityListPage.js.map +1 -1
  17. package/dist/pages/CreateWorkspacePage.js +24 -24
  18. package/dist/pages/CreateWorkspacePage.js.map +1 -1
  19. package/dist/pages/InviteAcceptancePage.js +46 -46
  20. package/dist/pages/InviteAcceptancePage.js.map +1 -1
  21. package/dist/pages/MembersListPage.js +27 -27
  22. package/dist/pages/MembersListPage.js.map +1 -1
  23. package/dist/pages/PendingInvitesPage.js +23 -23
  24. package/dist/pages/PendingInvitesPage.js.map +1 -1
  25. package/dist/pages/ProjectDetailPage.js +20 -20
  26. package/dist/pages/ProjectDetailPage.js.map +1 -1
  27. package/dist/pages/ProjectMembersPage.js +15 -15
  28. package/dist/pages/ProjectMembersPage.js.map +1 -1
  29. package/dist/pages/ProjectsListPage.js +16 -16
  30. package/dist/pages/ProjectsListPage.js.map +1 -1
  31. package/dist/pages/WorkspaceDetailPage.js +16 -16
  32. package/dist/pages/WorkspaceDetailPage.js.map +1 -1
  33. package/dist/pages/WorkspaceInvitePage.js +51 -51
  34. package/dist/pages/WorkspaceInvitePage.js.map +1 -1
  35. package/dist/pages/WorkspaceSettingsPage.js +2 -2
  36. package/dist/pages/WorkspaceSettingsPage.js.map +1 -1
  37. package/dist/pages/WorkspacesListPage.js +8 -8
  38. package/dist/pages/WorkspacesListPage.js.map +1 -1
  39. package/dist/pages/dashboard/DashboardCustomizer.js +3 -3
  40. package/dist/pages/dashboard/DashboardCustomizer.js.map +1 -1
  41. package/dist/pages/dashboard/DashboardGrid.js +2 -2
  42. package/dist/pages/dashboard/DashboardGrid.js.map +1 -1
  43. package/dist/pages/dashboard/DashboardPage.js +5 -5
  44. package/dist/pages/dashboard/DashboardPage.js.map +1 -1
  45. package/dist/pages/dashboard/WidgetWrapper.js +6 -6
  46. package/dist/pages/dashboard/WidgetWrapper.js.map +1 -1
  47. package/dist/pages/dashboard/widgets/AgentsWidget.js +6 -6
  48. package/dist/pages/dashboard/widgets/AgentsWidget.js.map +1 -1
  49. package/dist/pages/dashboard/widgets/GroupsWidget.js +3 -3
  50. package/dist/pages/dashboard/widgets/GroupsWidget.js.map +1 -1
  51. package/dist/pages/dashboard/widgets/MembersWidget.js +3 -3
  52. package/dist/pages/dashboard/widgets/MembersWidget.js.map +1 -1
  53. package/dist/pages/dashboard/widgets/ProjectsWidget.js +2 -2
  54. package/dist/pages/dashboard/widgets/ProjectsWidget.js.map +1 -1
  55. package/dist/pages/dashboard/widgets/RecentActivityWidget.js +3 -3
  56. package/dist/pages/dashboard/widgets/RecentActivityWidget.js.map +1 -1
  57. package/dist/pages/dashboard/widgets/SessionsWidget.js +4 -4
  58. package/dist/pages/dashboard/widgets/SessionsWidget.js.map +1 -1
  59. package/dist/pages/dashboard/widgets/TagCloudWidget.js +2 -2
  60. package/dist/pages/dashboard/widgets/TagCloudWidget.js.map +1 -1
  61. package/dist/pages/dashboard/widgets/TipsWidget.js +3 -3
  62. package/dist/pages/dashboard/widgets/TipsWidget.js.map +1 -1
  63. package/dist/pages/dashboard/widgets/UpcomingEventsWidget.js +2 -2
  64. package/dist/pages/dashboard/widgets/UpcomingEventsWidget.js.map +1 -1
  65. package/dist/pages/dashboard/widgets/VibesWidget.js +2 -2
  66. package/dist/pages/dashboard/widgets/VibesWidget.js.map +1 -1
  67. package/dist/pages/dashboard/widgets/WelcomeWidget.js +3 -3
  68. package/dist/pages/dashboard/widgets/WelcomeWidget.js.map +1 -1
  69. package/dist/pages/dashboard/widgets/WorkflowStatsWidget.js +4 -4
  70. package/dist/pages/dashboard/widgets/WorkflowStatsWidget.js.map +1 -1
  71. package/dist/pages/dashboard/widgets/WorkspacesWidget.js +2 -2
  72. package/dist/pages/dashboard/widgets/WorkspacesWidget.js.map +1 -1
  73. package/package.json +1 -1
@@ -111,7 +111,7 @@ function D() {
111
111
  className: "text-2xl sm:text-3xl font-bold",
112
112
  children: t(D, "projectsList.pageTitle", "Projects")
113
113
  }), /* @__PURE__ */ g("p", {
114
- className: "text-muted-foreground",
114
+ className: "text-text-secondary",
115
115
  children: t(D, "projectsList.pageSubtitle", "Manage and organize your projects")
116
116
  })] }), /* @__PURE__ */ _("div", {
117
117
  className: "flex items-center gap-2",
@@ -147,7 +147,7 @@ function D() {
147
147
  /* @__PURE__ */ _("div", {
148
148
  className: "relative flex-1",
149
149
  children: [/* @__PURE__ */ g(ie, {
150
- className: "absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground",
150
+ className: "absolute left-3 top-1/2 transform -translate-y-1/2 text-text-secondary",
151
151
  size: 18
152
152
  }), /* @__PURE__ */ g(oe, {
153
153
  placeholder: t(D, "projectsList.searchPlaceholder", "Search projects..."),
@@ -206,14 +206,14 @@ function D() {
206
206
  ]
207
207
  }),
208
208
  R.length > 0 && /* @__PURE__ */ _("div", {
209
- className: "flex flex-col gap-3 rounded-lg border border-border bg-card px-4 py-3",
209
+ className: "flex flex-col gap-3 rounded-lg border border-border-subtle bg-bg-surface px-4 py-3",
210
210
  children: [/* @__PURE__ */ _("div", {
211
211
  className: "flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",
212
212
  children: [/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("p", {
213
213
  className: "text-sm font-medium",
214
214
  children: t(D, "projectsList.activeTagFilters", "Active tag filters")
215
215
  }), /* @__PURE__ */ g("p", {
216
- className: "text-xs text-muted-foreground",
216
+ className: "text-xs text-text-secondary",
217
217
  children: t(D, "projectsList.activeTagFiltersDescription", "Click a project tag to add or remove it from the current filter set.")
218
218
  })] }), /* @__PURE__ */ _(x, {
219
219
  value: B ? "all" : "any",
@@ -244,16 +244,16 @@ function D() {
244
244
  })]
245
245
  }),
246
246
  G && /* @__PURE__ */ g(y, {
247
- className: "border-destructive",
247
+ className: "border-status-error-border",
248
248
  children: /* @__PURE__ */ g(b, {
249
249
  className: "pt-6",
250
250
  children: /* @__PURE__ */ _("div", {
251
251
  className: "flex flex-col items-center justify-center py-8 text-center",
252
252
  children: [/* @__PURE__ */ g("p", {
253
- className: "text-destructive font-semibold mb-2",
253
+ className: "text-status-error-text font-semibold mb-2",
254
254
  children: t(D, "projectsList.errorLoading", "Error loading projects")
255
255
  }), /* @__PURE__ */ g("p", {
256
- className: "text-sm text-muted-foreground",
256
+ className: "text-sm text-text-secondary",
257
257
  children: G instanceof Error ? G.message : t(D, "projectsList.unknownError", "An unknown error occurred")
258
258
  })]
259
259
  })
@@ -279,10 +279,10 @@ function D() {
279
279
  className: "flex flex-col items-center justify-center py-12 text-center",
280
280
  children: [
281
281
  /* @__PURE__ */ g("div", {
282
- className: "rounded-full bg-muted p-6 mb-4",
282
+ className: "rounded-full bg-bg-sunken p-6 mb-4",
283
283
  children: /* @__PURE__ */ g(te, {
284
284
  size: 48,
285
- className: "text-muted-foreground"
285
+ className: "text-text-secondary"
286
286
  })
287
287
  }),
288
288
  /* @__PURE__ */ g("h3", {
@@ -290,7 +290,7 @@ function D() {
290
290
  children: t(D, "projectsList.noProjectsFound", "No projects found")
291
291
  }),
292
292
  /* @__PURE__ */ g("p", {
293
- className: "text-muted-foreground max-w-sm mb-4",
293
+ className: "text-text-secondary max-w-sm mb-4",
294
294
  children: j.search ? t(D, "projectsList.adjustSearchOrFilters", "Try adjusting your search or filters") : t(D, "projectsList.createFirstProject", "Create your first project to get started organizing your work")
295
295
  }),
296
296
  !j.search && ye && /* @__PURE__ */ _(v, {
@@ -305,9 +305,9 @@ function D() {
305
305
  }) }),
306
306
  !W && !G && K && K.items.length > 0 && /* @__PURE__ */ _(ae, { children: [
307
307
  /* @__PURE__ */ _("div", {
308
- className: "flex flex-col gap-3 rounded-lg border bg-card p-4 sm:flex-row sm:items-center sm:justify-between",
308
+ className: "flex flex-col gap-3 rounded-lg border bg-bg-surface p-4 sm:flex-row sm:items-center sm:justify-between",
309
309
  children: [/* @__PURE__ */ _("label", {
310
- className: "flex items-center gap-2 text-sm text-foreground",
310
+ className: "flex items-center gap-2 text-sm text-text-primary",
311
311
  children: [/* @__PURE__ */ g("input", {
312
312
  type: "checkbox",
313
313
  checked: X,
@@ -316,13 +316,13 @@ function D() {
316
316
  },
317
317
  onChange: (e) => Te(e.target.checked),
318
318
  "aria-label": t(D, "projectsList.selectAllAriaLabel", "Select all projects"),
319
- className: "size-4 rounded border-border text-primary"
319
+ className: "size-4 rounded border-border-subtle text-primary"
320
320
  }), /* @__PURE__ */ g("span", { children: t(D, "projectsList.selectAllVisible", "Select all visible") })]
321
321
  }), /* @__PURE__ */ _("div", {
322
322
  className: "flex flex-wrap items-center gap-2",
323
323
  children: [
324
324
  /* @__PURE__ */ _("span", {
325
- className: "text-sm text-muted-foreground",
325
+ className: "text-sm text-text-secondary",
326
326
  children: [
327
327
  N.size,
328
328
  " ",
@@ -368,7 +368,7 @@ function D() {
368
368
  (n === null ? window.confirm(e) : n) && (await Promise.allSettled(q.map((e) => _e(e.id))), F());
369
369
  },
370
370
  disabled: q.length === 0,
371
- className: "text-destructive",
371
+ className: "text-status-error-text",
372
372
  children: [t(D, "projectsList.deleteSelected", "Delete Selected"), q.length > 0 ? ` (${q.length})` : ""]
373
373
  })
374
374
  ]
@@ -388,7 +388,7 @@ function D() {
388
388
  }, e.id))
389
389
  }),
390
390
  K.total > K.items.length && /* @__PURE__ */ _("div", {
391
- className: "text-center text-sm text-muted-foreground",
391
+ className: "text-center text-sm text-text-secondary",
392
392
  children: [
393
393
  t(D, "projectsList.showingPrefix", "Showing"),
394
394
  " ",
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectsListPage.js","names":[],"sources":["../../src/pages/ProjectsListPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { FolderKanban, Plus, Grid3x3, List, Search } from 'lucide-react';\nimport { TagBadge } from '@burdenoff/fe-libs/tag';\nimport {\n Button,\n Input,\n Card,\n CardContent,\n PageContainer,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Skeleton,\n} from '@burdenoff/fe-libs/ui';\nimport { useDebounce } from '@burdenoff/fe-libs/shared/hooks';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useWorkspacesStore } from '../store/workspacesStore';\nimport { nativeConfirm, nativeImpact } from '../utils/nativeBridge';\nimport { useMyProjects } from '../hooks/useProjects';\nimport {\n useArchiveProject,\n useDeleteProject,\n useUnarchiveProject,\n} from '../hooks/useProjectMutations';\nimport { ProjectCard } from '../components/ProjectCard';\nimport { CreateProjectDialog } from '../components/CreateProjectDialog';\nimport { useWorkspacePermissions } from '../hooks/useWorkspacePermissions';\nimport { useWorkspacesPageView } from '../hooks/useWorkspacesPageView';\nimport { tWithFallback } from '../utils/i18n';\nimport { getProjectDisplayTags } from '../utils/projectTags';\nimport type { ProjectTag, Workspace } from '../types';\n\nexport function ProjectsListPage() {\n const { t } = useI18n();\n\n const { workspaceId } = useParams<{ workspaceId: string }>();\n useWorkspacesPageView('workspaces.projects', { workspaceId, requireParams: true });\n const {\n viewMode,\n setViewMode,\n projectFilters,\n setProjectFilters,\n clearProjectFilters,\n selectedProjectIds,\n toggleProjectSelection,\n clearProjectSelection,\n openCreateProjectDialog,\n isCreateProjectDialogOpen,\n } = useWorkspacesStore();\n\n const [searchValue, setSearchValue] = useState(projectFilters.search || '');\n const [activeTagFilters, setActiveTagFilters] = useState<ProjectTag[]>([]);\n const [matchAllTagFilters, setMatchAllTagFilters] = useState(true);\n const debouncedSearch = useDebounce(searchValue, 500);\n\n // Update filters when debounced search changes\n useEffect(() => {\n setProjectFilters({ search: debouncedSearch });\n }, [debouncedSearch, setProjectFilters]);\n\n useEffect(() => {\n if (activeTagFilters.length === 0) {\n setProjectFilters({\n tagIds: undefined,\n tagHierarchyPaths: undefined,\n matchAllTags: undefined,\n includeDescendants: undefined,\n });\n return;\n }\n\n setProjectFilters({\n tagIds: activeTagFilters.map((tag) => tag.tagId),\n tagHierarchyPaths: activeTagFilters\n .map((tag) => tag.hierarchyPath)\n .filter((tagPath): tagPath is string => typeof tagPath === 'string' && tagPath.length > 0),\n matchAllTags: matchAllTagFilters,\n includeDescendants: true,\n });\n }, [activeTagFilters, matchAllTagFilters, setProjectFilters]);\n\n useEffect(() => {\n return () => clearProjectSelection();\n }, [clearProjectSelection]);\n\n // Membership-scoped query: backend filters by actorId, so no client-side\n // permission gate is needed. Page is shared by `/workspaces/projects` and\n // `/workspaces/:workspaceId/projects`; the latter narrows the list below.\n const PROJECTS_PAGE_SIZE = 500;\n const {\n data: myData,\n isLoading,\n error,\n } = useMyProjects({\n limit: PROJECTS_PAGE_SIZE,\n offset: 0,\n });\n\n const data = useMemo(() => {\n const all = myData?.items ?? [];\n const q = (projectFilters.search ?? '').trim().toLowerCase();\n const tagIds = projectFilters.tagIds ?? [];\n const matchAllTags = projectFilters.matchAllTags ?? true;\n\n const matchesWorkspace = (project: (typeof all)[number]) =>\n !workspaceId || project.workspaceId === workspaceId;\n const matchesSearch = (project: (typeof all)[number]) =>\n !q || project.name.toLowerCase().includes(q) || project.key.toLowerCase().includes(q);\n const matchesStatus = (project: (typeof all)[number]) =>\n !projectFilters.status || project.status === projectFilters.status;\n const matchesArchived = (project: (typeof all)[number]) =>\n typeof projectFilters.archived === 'undefined' ||\n project.archived === projectFilters.archived;\n const matchesTags = (project: (typeof all)[number]) => {\n if (tagIds.length === 0) return true;\n const projectTagIds = new Set(getProjectDisplayTags(project).map((t) => t.tagId));\n return matchAllTags\n ? tagIds.every((id) => projectTagIds.has(id))\n : tagIds.some((id) => projectTagIds.has(id));\n };\n\n const filtered = all.filter(\n (project) =>\n matchesWorkspace(project) &&\n matchesSearch(project) &&\n matchesStatus(project) &&\n matchesArchived(project) &&\n matchesTags(project)\n );\n\n // Surface the real backend totals so the \"Showing X of Y\" footer can flag\n // truncation. For narrowed views (workspace/tag filter), the backend total\n // doesn't reflect the post-filter count, so we report the filtered size as\n // total. But we MUST preserve `hasMore` from upstream — if the upstream\n // query was capped (hasMore=true), matches outside the first page are\n // silently hidden, and the footer needs to keep flagging that.\n const apiTotal = myData?.total ?? filtered.length;\n const apiHasMore = myData?.hasMore ?? false;\n const isNarrowed = !!workspaceId || tagIds.length > 0;\n return {\n items: filtered,\n total: isNarrowed ? filtered.length : apiTotal,\n hasMore: apiHasMore,\n };\n }, [myData?.items, myData?.total, myData?.hasMore, projectFilters, workspaceId]);\n\n const { mutateAsync: archiveProject } = useArchiveProject(workspaceId);\n const { mutateAsync: unarchiveProject } = useUnarchiveProject(workspaceId);\n const { mutateAsync: deleteProject } = useDeleteProject(workspaceId);\n\n const selectedProjects = useMemo(\n () => data?.items.filter((project) => selectedProjectIds.has(project.id)) ?? [],\n [data?.items, selectedProjectIds]\n );\n\n const archivableProjects = useMemo(\n () => selectedProjects.filter((project) => !project.archived),\n [selectedProjects]\n );\n\n const unarchivableProjects = useMemo(\n () => selectedProjects.filter((project) => project.archived),\n [selectedProjects]\n );\n\n const allVisibleSelected =\n (data?.items.length ?? 0) > 0 &&\n (data?.items ?? []).every((project) => selectedProjectIds.has(project.id));\n const partiallySelected =\n (data?.items ?? []).some((project) => selectedProjectIds.has(project.id)) &&\n !allVisibleSelected;\n\n const {\n canCreateProject,\n canDeleteProject,\n canArchiveProject,\n isLoading: permissionsLoading,\n } = useWorkspacePermissions();\n\n if (permissionsLoading) {\n return (\n <div className=\"space-y-4 p-6\">\n <Skeleton className=\"h-10 w-1/3\" />\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-4\">\n <Skeleton className=\"h-32\" />\n <Skeleton className=\"h-32\" />\n <Skeleton className=\"h-32\" />\n </div>\n </div>\n );\n }\n\n // No client-side list gate: this page calls `myProjects` which is\n // membership-scoped — the resolver filters by actorId on the backend, so the\n // data set returned IS the access list. If the user is in zero projects, the\n // page renders the empty state below. Per-action checks (canCreateProject,\n // canDeleteProject, canArchiveProject) remain in place to hide buttons the\n // user isn't authorized for on their workspace.\n\n const handleCreateProject = () => {\n openCreateProjectDialog({} as Workspace);\n };\n\n const handleStatusFilter = (value: string) => {\n if (value === 'all') {\n const nextFilters = { ...projectFilters };\n delete nextFilters.status;\n setProjectFilters(nextFilters);\n } else {\n setProjectFilters({ status: value as 'ACTIVE' | 'INACTIVE' | 'ARCHIVED' });\n }\n };\n\n const handleArchivedFilter = (value: string) => {\n if (value === 'all') {\n const nextFilters = { ...projectFilters };\n delete nextFilters.archived;\n setProjectFilters(nextFilters);\n } else {\n setProjectFilters({ archived: value === 'true' });\n }\n };\n\n const toggleTagFilter = (tag: ProjectTag) => {\n setActiveTagFilters((current) => {\n const exists = current.some((entry) => entry.tagId === tag.tagId);\n if (exists) {\n return current.filter((entry) => entry.tagId !== tag.tagId);\n }\n\n return [...current, tag];\n });\n };\n\n const clearAllProjectFilters = () => {\n setSearchValue('');\n setActiveTagFilters([]);\n setMatchAllTagFilters(true);\n clearProjectFilters();\n };\n\n const toggleAllProjects = (checked: boolean) => {\n (data?.items ?? []).forEach((project) => {\n const isSelected = selectedProjectIds.has(project.id);\n if (checked !== isSelected) {\n toggleProjectSelection(project.id);\n }\n });\n };\n\n const handleBulkArchive = async () => {\n if (archivableProjects.length === 0) return;\n await Promise.allSettled(archivableProjects.map((project) => archiveProject(project.id)));\n clearProjectSelection();\n };\n\n const handleBulkUnarchive = async () => {\n if (unarchivableProjects.length === 0) return;\n await Promise.allSettled(unarchivableProjects.map((project) => unarchiveProject(project.id)));\n clearProjectSelection();\n };\n\n const handleBulkDelete = async () => {\n if (selectedProjects.length === 0) return;\n void nativeImpact('medium');\n const message =\n tWithFallback(t, 'projectsList.bulkDeleteConfirmPrefix', 'Delete ') +\n selectedProjects.length +\n ' ' +\n tWithFallback(t, 'projectsList.bulkDeleteConfirmProject', 'selected project') +\n (selectedProjects.length === 1\n ? ''\n : tWithFallback(t, 'projectsList.bulkDeleteConfirmPluralSuffix', 's')) +\n tWithFallback(t, 'projectsList.bulkDeleteConfirmSuffix', '? This cannot be undone.');\n const native = await nativeConfirm({\n title: 'Delete projects',\n message,\n okButtonTitle: 'Delete',\n cancelButtonTitle: 'Cancel',\n });\n const confirmed = native === null ? window.confirm(message) : native;\n if (!confirmed) return;\n\n await Promise.allSettled(selectedProjects.map((project) => deleteProject(project.id)));\n clearProjectSelection();\n };\n\n return (\n <PageContainer padded={false}>\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n {/* Header */}\n <div className=\"flex flex-col sm:flex-row sm:items-center justify-between gap-4\">\n <div>\n <h1 className=\"text-2xl sm:text-3xl font-bold\">\n {tWithFallback(t, 'projectsList.pageTitle', 'Projects')}\n </h1>\n <p className=\"text-muted-foreground\">\n {tWithFallback(t, 'projectsList.pageSubtitle', 'Manage and organize your projects')}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n {/* View mode toggle */}\n <div className=\"flex items-center gap-1 border rounded-lg p-1\">\n <Button\n variant={viewMode === 'grid' ? 'secondary' : 'ghost'}\n size=\"sm\"\n onClick={() => setViewMode('grid')}\n className=\"size-8 p-0\"\n aria-label={tWithFallback(\n t,\n 'projectsList.gridViewAriaLabel',\n 'Show projects in grid view'\n )}\n >\n <Grid3x3 size={16} />\n </Button>\n <Button\n variant={viewMode === 'list' ? 'secondary' : 'ghost'}\n size=\"sm\"\n onClick={() => setViewMode('list')}\n className=\"size-8 p-0\"\n aria-label={tWithFallback(\n t,\n 'projectsList.listViewAriaLabel',\n 'Show projects in list view'\n )}\n >\n <List size={16} />\n </Button>\n </div>\n {canCreateProject && (\n <Button onClick={handleCreateProject}>\n <Plus className=\"size-4 sm:mr-2\" />\n <span className=\"hidden sm:inline\">\n {tWithFallback(t, 'projectsList.createProject', 'Create Project')}\n </span>\n </Button>\n )}\n </div>\n </div>\n\n {/* Filters & Search */}\n <div className=\"flex flex-col sm:flex-row gap-3\">\n <div className=\"relative flex-1\">\n <Search\n className=\"absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground\"\n size={18}\n />\n <Input\n placeholder={tWithFallback(t, 'projectsList.searchPlaceholder', 'Search projects...')}\n value={searchValue}\n onChange={(e) => setSearchValue(e.target.value)}\n className=\"pl-[3.25rem]\"\n />\n </div>\n <Select value={projectFilters.status || 'all'} onValueChange={handleStatusFilter}>\n <SelectTrigger className=\"w-full sm:w-[180px]\">\n <SelectValue\n placeholder={tWithFallback(t, 'projectsList.filterByStatus', 'Filter by status')}\n />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">\n {tWithFallback(t, 'projectsList.statusAll', 'All Statuses')}\n </SelectItem>\n <SelectItem value=\"ACTIVE\">\n {tWithFallback(t, 'projectsList.statusActive', 'Active')}\n </SelectItem>\n <SelectItem value=\"INACTIVE\">\n {tWithFallback(t, 'projectsList.statusInactive', 'Inactive')}\n </SelectItem>\n </SelectContent>\n </Select>\n <Select\n value={\n projectFilters.archived === undefined\n ? 'all'\n : projectFilters.archived\n ? 'true'\n : 'false'\n }\n onValueChange={handleArchivedFilter}\n >\n <SelectTrigger className=\"w-full sm:w-[180px]\">\n <SelectValue\n placeholder={tWithFallback(t, 'projectsList.filterByArchive', 'Filter by archive')}\n />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">\n {tWithFallback(t, 'projectsList.archiveAll', 'All Projects')}\n </SelectItem>\n <SelectItem value=\"false\">\n {tWithFallback(t, 'projectsList.archiveActiveProjects', 'Active Projects')}\n </SelectItem>\n <SelectItem value=\"true\">\n {tWithFallback(t, 'projectsList.archiveArchived', 'Archived')}\n </SelectItem>\n </SelectContent>\n </Select>\n {(projectFilters.search ||\n projectFilters.status ||\n projectFilters.archived !== undefined ||\n activeTagFilters.length > 0) && (\n <Button variant=\"outline\" onClick={clearAllProjectFilters}>\n {tWithFallback(t, 'projectsList.clearFilters', 'Clear Filters')}\n </Button>\n )}\n </div>\n\n {activeTagFilters.length > 0 && (\n <div className=\"flex flex-col gap-3 rounded-lg border border-border bg-card px-4 py-3\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <p className=\"text-sm font-medium\">\n {tWithFallback(t, 'projectsList.activeTagFilters', 'Active tag filters')}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n {tWithFallback(\n t,\n 'projectsList.activeTagFiltersDescription',\n 'Click a project tag to add or remove it from the current filter set.'\n )}\n </p>\n </div>\n <Select\n value={matchAllTagFilters ? 'all' : 'any'}\n onValueChange={(value) => setMatchAllTagFilters(value === 'all')}\n >\n <SelectTrigger className=\"w-full sm:w-[180px]\">\n <SelectValue\n placeholder={tWithFallback(t, 'projectsList.tagMatchMode', 'Tag match mode')}\n />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">\n {tWithFallback(t, 'projectsList.tagMatchAll', 'Match all tags')}\n </SelectItem>\n <SelectItem value=\"any\">\n {tWithFallback(t, 'projectsList.tagMatchAny', 'Match any tag')}\n </SelectItem>\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"flex flex-wrap gap-2\">\n {activeTagFilters.map((tag) => (\n <TagBadge\n key={tag.tagId}\n id={tag.tagId}\n label={tag.label}\n color={tag.color ?? undefined}\n icon={tag.icon ?? undefined}\n description={tag.description ?? undefined}\n removable\n onRemove={() => toggleTagFilter(tag)}\n size=\"sm\"\n />\n ))}\n </div>\n </div>\n )}\n\n {/* Error State */}\n {error && (\n <Card className=\"border-destructive\">\n <CardContent className=\"pt-6\">\n <div className=\"flex flex-col items-center justify-center py-8 text-center\">\n <p className=\"text-destructive font-semibold mb-2\">\n {tWithFallback(t, 'projectsList.errorLoading', 'Error loading projects')}\n </p>\n <p className=\"text-sm text-muted-foreground\">\n {error instanceof Error\n ? error.message\n : tWithFallback(t, 'projectsList.unknownError', 'An unknown error occurred')}\n </p>\n </div>\n </CardContent>\n </Card>\n )}\n\n {/* Loading State */}\n {isLoading && (\n <div className=\"grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4\">\n {Array.from({ length: 6 }).map((_, i) => (\n <Card key={i}>\n <CardContent className=\"pt-6\">\n <div className=\"space-y-3\">\n <Skeleton className=\"size-12 rounded-lg\" />\n <Skeleton className=\"h-4 w-3/4\" />\n <Skeleton className=\"h-4 w-1/2\" />\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n )}\n\n {/* Empty State */}\n {!isLoading && !error && data?.items.length === 0 && (\n <Card>\n <CardContent className=\"pt-6\">\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <div className=\"rounded-full bg-muted p-6 mb-4\">\n <FolderKanban size={48} className=\"text-muted-foreground\" />\n </div>\n <h3 className=\"text-lg font-semibold mb-2\">\n {tWithFallback(t, 'projectsList.noProjectsFound', 'No projects found')}\n </h3>\n <p className=\"text-muted-foreground max-w-sm mb-4\">\n {projectFilters.search\n ? tWithFallback(\n t,\n 'projectsList.adjustSearchOrFilters',\n 'Try adjusting your search or filters'\n )\n : tWithFallback(\n t,\n 'projectsList.createFirstProject',\n 'Create your first project to get started organizing your work'\n )}\n </p>\n {!projectFilters.search && canCreateProject && (\n <Button onClick={handleCreateProject}>\n <Plus size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectsList.createProject', 'Create Project')}\n </Button>\n )}\n </div>\n </CardContent>\n </Card>\n )}\n\n {/* Projects Grid */}\n {!isLoading && !error && data && data.items.length > 0 && (\n <>\n <div className=\"flex flex-col gap-3 rounded-lg border bg-card p-4 sm:flex-row sm:items-center sm:justify-between\">\n <label className=\"flex items-center gap-2 text-sm text-foreground\">\n <input\n type=\"checkbox\"\n checked={allVisibleSelected}\n ref={(input) => {\n if (input) {\n input.indeterminate = partiallySelected && !allVisibleSelected;\n }\n }}\n onChange={(event) => toggleAllProjects(event.target.checked)}\n aria-label={tWithFallback(\n t,\n 'projectsList.selectAllAriaLabel',\n 'Select all projects'\n )}\n className=\"size-4 rounded border-border text-primary\"\n />\n <span>\n {tWithFallback(t, 'projectsList.selectAllVisible', 'Select all visible')}\n </span>\n </label>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm text-muted-foreground\">\n {selectedProjectIds.size} {tWithFallback(t, 'projectsList.selected', 'selected')}\n </span>\n {selectedProjectIds.size > 0 && (\n <Button variant=\"ghost\" size=\"sm\" onClick={clearProjectSelection}>\n {tWithFallback(t, 'projectsList.clearSelection', 'Clear selection')}\n </Button>\n )}\n {/* Bulk mutations are bound to a single workspaceId via the\n archive/unarchive/delete hooks. On the cross-workspace\n /workspaces/projects route (workspaceId is undefined),\n hide them — selections can span workspaces and the bound\n hook would fail authorization for projects outside the\n caller's context. The per-card actions in ProjectCard\n still work because they resolve per-project. */}\n {workspaceId && canArchiveProject && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleBulkArchive}\n disabled={archivableProjects.length === 0}\n >\n {tWithFallback(t, 'projectsList.archiveSelected', 'Archive Selected')}\n {archivableProjects.length > 0 ? ` (${archivableProjects.length})` : ''}\n </Button>\n )}\n {workspaceId && canArchiveProject && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleBulkUnarchive}\n disabled={unarchivableProjects.length === 0}\n >\n {tWithFallback(t, 'projectsList.unarchiveSelected', 'Unarchive Selected')}\n {unarchivableProjects.length > 0 ? ` (${unarchivableProjects.length})` : ''}\n </Button>\n )}\n {workspaceId && canDeleteProject && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleBulkDelete}\n disabled={selectedProjects.length === 0}\n className=\"text-destructive\"\n >\n {tWithFallback(t, 'projectsList.deleteSelected', 'Delete Selected')}\n {selectedProjects.length > 0 ? ` (${selectedProjects.length})` : ''}\n </Button>\n )}\n </div>\n </div>\n\n <div\n className={\n viewMode === 'list'\n ? 'space-y-3'\n : 'grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4'\n }\n >\n {data.items.map((project) => (\n <ProjectCard\n key={project.id}\n project={project}\n selected={selectedProjectIds.has(project.id)}\n canArchive={canArchiveProject}\n canDelete={canDeleteProject}\n onTagClick={toggleTagFilter}\n onToggleSelect={(checked) => {\n const isSelected = selectedProjectIds.has(project.id);\n if (checked !== isSelected) {\n toggleProjectSelection(project.id);\n }\n }}\n />\n ))}\n </div>\n\n {/* Pagination Info */}\n {data.total > data.items.length && (\n <div className=\"text-center text-sm text-muted-foreground\">\n {tWithFallback(t, 'projectsList.showingPrefix', 'Showing')} {data.items.length}{' '}\n {tWithFallback(t, 'projectsList.showingOf', 'of')} {data.total}{' '}\n {tWithFallback(t, 'projectsList.showingProjects', 'projects')}\n </div>\n )}\n </>\n )}\n\n {/* Create Project Dialog */}\n {isCreateProjectDialogOpen && <CreateProjectDialog workspaceId={workspaceId} />}\n </div>\n </PageContainer>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ProjectsListPage.js","names":[],"sources":["../../src/pages/ProjectsListPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { FolderKanban, Plus, Grid3x3, List, Search } from 'lucide-react';\nimport { TagBadge } from '@burdenoff/fe-libs/tag';\nimport {\n Button,\n Input,\n Card,\n CardContent,\n PageContainer,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Skeleton,\n} from '@burdenoff/fe-libs/ui';\nimport { useDebounce } from '@burdenoff/fe-libs/shared/hooks';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useWorkspacesStore } from '../store/workspacesStore';\nimport { nativeConfirm, nativeImpact } from '../utils/nativeBridge';\nimport { useMyProjects } from '../hooks/useProjects';\nimport {\n useArchiveProject,\n useDeleteProject,\n useUnarchiveProject,\n} from '../hooks/useProjectMutations';\nimport { ProjectCard } from '../components/ProjectCard';\nimport { CreateProjectDialog } from '../components/CreateProjectDialog';\nimport { useWorkspacePermissions } from '../hooks/useWorkspacePermissions';\nimport { useWorkspacesPageView } from '../hooks/useWorkspacesPageView';\nimport { tWithFallback } from '../utils/i18n';\nimport { getProjectDisplayTags } from '../utils/projectTags';\nimport type { ProjectTag, Workspace } from '../types';\n\nexport function ProjectsListPage() {\n const { t } = useI18n();\n\n const { workspaceId } = useParams<{ workspaceId: string }>();\n useWorkspacesPageView('workspaces.projects', { workspaceId, requireParams: true });\n const {\n viewMode,\n setViewMode,\n projectFilters,\n setProjectFilters,\n clearProjectFilters,\n selectedProjectIds,\n toggleProjectSelection,\n clearProjectSelection,\n openCreateProjectDialog,\n isCreateProjectDialogOpen,\n } = useWorkspacesStore();\n\n const [searchValue, setSearchValue] = useState(projectFilters.search || '');\n const [activeTagFilters, setActiveTagFilters] = useState<ProjectTag[]>([]);\n const [matchAllTagFilters, setMatchAllTagFilters] = useState(true);\n const debouncedSearch = useDebounce(searchValue, 500);\n\n // Update filters when debounced search changes\n useEffect(() => {\n setProjectFilters({ search: debouncedSearch });\n }, [debouncedSearch, setProjectFilters]);\n\n useEffect(() => {\n if (activeTagFilters.length === 0) {\n setProjectFilters({\n tagIds: undefined,\n tagHierarchyPaths: undefined,\n matchAllTags: undefined,\n includeDescendants: undefined,\n });\n return;\n }\n\n setProjectFilters({\n tagIds: activeTagFilters.map((tag) => tag.tagId),\n tagHierarchyPaths: activeTagFilters\n .map((tag) => tag.hierarchyPath)\n .filter((tagPath): tagPath is string => typeof tagPath === 'string' && tagPath.length > 0),\n matchAllTags: matchAllTagFilters,\n includeDescendants: true,\n });\n }, [activeTagFilters, matchAllTagFilters, setProjectFilters]);\n\n useEffect(() => {\n return () => clearProjectSelection();\n }, [clearProjectSelection]);\n\n // Membership-scoped query: backend filters by actorId, so no client-side\n // permission gate is needed. Page is shared by `/workspaces/projects` and\n // `/workspaces/:workspaceId/projects`; the latter narrows the list below.\n const PROJECTS_PAGE_SIZE = 500;\n const {\n data: myData,\n isLoading,\n error,\n } = useMyProjects({\n limit: PROJECTS_PAGE_SIZE,\n offset: 0,\n });\n\n const data = useMemo(() => {\n const all = myData?.items ?? [];\n const q = (projectFilters.search ?? '').trim().toLowerCase();\n const tagIds = projectFilters.tagIds ?? [];\n const matchAllTags = projectFilters.matchAllTags ?? true;\n\n const matchesWorkspace = (project: (typeof all)[number]) =>\n !workspaceId || project.workspaceId === workspaceId;\n const matchesSearch = (project: (typeof all)[number]) =>\n !q || project.name.toLowerCase().includes(q) || project.key.toLowerCase().includes(q);\n const matchesStatus = (project: (typeof all)[number]) =>\n !projectFilters.status || project.status === projectFilters.status;\n const matchesArchived = (project: (typeof all)[number]) =>\n typeof projectFilters.archived === 'undefined' ||\n project.archived === projectFilters.archived;\n const matchesTags = (project: (typeof all)[number]) => {\n if (tagIds.length === 0) return true;\n const projectTagIds = new Set(getProjectDisplayTags(project).map((t) => t.tagId));\n return matchAllTags\n ? tagIds.every((id) => projectTagIds.has(id))\n : tagIds.some((id) => projectTagIds.has(id));\n };\n\n const filtered = all.filter(\n (project) =>\n matchesWorkspace(project) &&\n matchesSearch(project) &&\n matchesStatus(project) &&\n matchesArchived(project) &&\n matchesTags(project)\n );\n\n // Surface the real backend totals so the \"Showing X of Y\" footer can flag\n // truncation. For narrowed views (workspace/tag filter), the backend total\n // doesn't reflect the post-filter count, so we report the filtered size as\n // total. But we MUST preserve `hasMore` from upstream — if the upstream\n // query was capped (hasMore=true), matches outside the first page are\n // silently hidden, and the footer needs to keep flagging that.\n const apiTotal = myData?.total ?? filtered.length;\n const apiHasMore = myData?.hasMore ?? false;\n const isNarrowed = !!workspaceId || tagIds.length > 0;\n return {\n items: filtered,\n total: isNarrowed ? filtered.length : apiTotal,\n hasMore: apiHasMore,\n };\n }, [myData?.items, myData?.total, myData?.hasMore, projectFilters, workspaceId]);\n\n const { mutateAsync: archiveProject } = useArchiveProject(workspaceId);\n const { mutateAsync: unarchiveProject } = useUnarchiveProject(workspaceId);\n const { mutateAsync: deleteProject } = useDeleteProject(workspaceId);\n\n const selectedProjects = useMemo(\n () => data?.items.filter((project) => selectedProjectIds.has(project.id)) ?? [],\n [data?.items, selectedProjectIds]\n );\n\n const archivableProjects = useMemo(\n () => selectedProjects.filter((project) => !project.archived),\n [selectedProjects]\n );\n\n const unarchivableProjects = useMemo(\n () => selectedProjects.filter((project) => project.archived),\n [selectedProjects]\n );\n\n const allVisibleSelected =\n (data?.items.length ?? 0) > 0 &&\n (data?.items ?? []).every((project) => selectedProjectIds.has(project.id));\n const partiallySelected =\n (data?.items ?? []).some((project) => selectedProjectIds.has(project.id)) &&\n !allVisibleSelected;\n\n const {\n canCreateProject,\n canDeleteProject,\n canArchiveProject,\n isLoading: permissionsLoading,\n } = useWorkspacePermissions();\n\n if (permissionsLoading) {\n return (\n <div className=\"space-y-4 p-6\">\n <Skeleton className=\"h-10 w-1/3\" />\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-4\">\n <Skeleton className=\"h-32\" />\n <Skeleton className=\"h-32\" />\n <Skeleton className=\"h-32\" />\n </div>\n </div>\n );\n }\n\n // No client-side list gate: this page calls `myProjects` which is\n // membership-scoped — the resolver filters by actorId on the backend, so the\n // data set returned IS the access list. If the user is in zero projects, the\n // page renders the empty state below. Per-action checks (canCreateProject,\n // canDeleteProject, canArchiveProject) remain in place to hide buttons the\n // user isn't authorized for on their workspace.\n\n const handleCreateProject = () => {\n openCreateProjectDialog({} as Workspace);\n };\n\n const handleStatusFilter = (value: string) => {\n if (value === 'all') {\n const nextFilters = { ...projectFilters };\n delete nextFilters.status;\n setProjectFilters(nextFilters);\n } else {\n setProjectFilters({ status: value as 'ACTIVE' | 'INACTIVE' | 'ARCHIVED' });\n }\n };\n\n const handleArchivedFilter = (value: string) => {\n if (value === 'all') {\n const nextFilters = { ...projectFilters };\n delete nextFilters.archived;\n setProjectFilters(nextFilters);\n } else {\n setProjectFilters({ archived: value === 'true' });\n }\n };\n\n const toggleTagFilter = (tag: ProjectTag) => {\n setActiveTagFilters((current) => {\n const exists = current.some((entry) => entry.tagId === tag.tagId);\n if (exists) {\n return current.filter((entry) => entry.tagId !== tag.tagId);\n }\n\n return [...current, tag];\n });\n };\n\n const clearAllProjectFilters = () => {\n setSearchValue('');\n setActiveTagFilters([]);\n setMatchAllTagFilters(true);\n clearProjectFilters();\n };\n\n const toggleAllProjects = (checked: boolean) => {\n (data?.items ?? []).forEach((project) => {\n const isSelected = selectedProjectIds.has(project.id);\n if (checked !== isSelected) {\n toggleProjectSelection(project.id);\n }\n });\n };\n\n const handleBulkArchive = async () => {\n if (archivableProjects.length === 0) return;\n await Promise.allSettled(archivableProjects.map((project) => archiveProject(project.id)));\n clearProjectSelection();\n };\n\n const handleBulkUnarchive = async () => {\n if (unarchivableProjects.length === 0) return;\n await Promise.allSettled(unarchivableProjects.map((project) => unarchiveProject(project.id)));\n clearProjectSelection();\n };\n\n const handleBulkDelete = async () => {\n if (selectedProjects.length === 0) return;\n void nativeImpact('medium');\n const message =\n tWithFallback(t, 'projectsList.bulkDeleteConfirmPrefix', 'Delete ') +\n selectedProjects.length +\n ' ' +\n tWithFallback(t, 'projectsList.bulkDeleteConfirmProject', 'selected project') +\n (selectedProjects.length === 1\n ? ''\n : tWithFallback(t, 'projectsList.bulkDeleteConfirmPluralSuffix', 's')) +\n tWithFallback(t, 'projectsList.bulkDeleteConfirmSuffix', '? This cannot be undone.');\n const native = await nativeConfirm({\n title: 'Delete projects',\n message,\n okButtonTitle: 'Delete',\n cancelButtonTitle: 'Cancel',\n });\n const confirmed = native === null ? window.confirm(message) : native;\n if (!confirmed) return;\n\n await Promise.allSettled(selectedProjects.map((project) => deleteProject(project.id)));\n clearProjectSelection();\n };\n\n return (\n <PageContainer padded={false}>\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n {/* Header */}\n <div className=\"flex flex-col sm:flex-row sm:items-center justify-between gap-4\">\n <div>\n <h1 className=\"text-2xl sm:text-3xl font-bold\">\n {tWithFallback(t, 'projectsList.pageTitle', 'Projects')}\n </h1>\n <p className=\"text-text-secondary\">\n {tWithFallback(t, 'projectsList.pageSubtitle', 'Manage and organize your projects')}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n {/* View mode toggle */}\n <div className=\"flex items-center gap-1 border rounded-lg p-1\">\n <Button\n variant={viewMode === 'grid' ? 'secondary' : 'ghost'}\n size=\"sm\"\n onClick={() => setViewMode('grid')}\n className=\"size-8 p-0\"\n aria-label={tWithFallback(\n t,\n 'projectsList.gridViewAriaLabel',\n 'Show projects in grid view'\n )}\n >\n <Grid3x3 size={16} />\n </Button>\n <Button\n variant={viewMode === 'list' ? 'secondary' : 'ghost'}\n size=\"sm\"\n onClick={() => setViewMode('list')}\n className=\"size-8 p-0\"\n aria-label={tWithFallback(\n t,\n 'projectsList.listViewAriaLabel',\n 'Show projects in list view'\n )}\n >\n <List size={16} />\n </Button>\n </div>\n {canCreateProject && (\n <Button onClick={handleCreateProject}>\n <Plus className=\"size-4 sm:mr-2\" />\n <span className=\"hidden sm:inline\">\n {tWithFallback(t, 'projectsList.createProject', 'Create Project')}\n </span>\n </Button>\n )}\n </div>\n </div>\n\n {/* Filters & Search */}\n <div className=\"flex flex-col sm:flex-row gap-3\">\n <div className=\"relative flex-1\">\n <Search\n className=\"absolute left-3 top-1/2 transform -translate-y-1/2 text-text-secondary\"\n size={18}\n />\n <Input\n placeholder={tWithFallback(t, 'projectsList.searchPlaceholder', 'Search projects...')}\n value={searchValue}\n onChange={(e) => setSearchValue(e.target.value)}\n className=\"pl-[3.25rem]\"\n />\n </div>\n <Select value={projectFilters.status || 'all'} onValueChange={handleStatusFilter}>\n <SelectTrigger className=\"w-full sm:w-[180px]\">\n <SelectValue\n placeholder={tWithFallback(t, 'projectsList.filterByStatus', 'Filter by status')}\n />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">\n {tWithFallback(t, 'projectsList.statusAll', 'All Statuses')}\n </SelectItem>\n <SelectItem value=\"ACTIVE\">\n {tWithFallback(t, 'projectsList.statusActive', 'Active')}\n </SelectItem>\n <SelectItem value=\"INACTIVE\">\n {tWithFallback(t, 'projectsList.statusInactive', 'Inactive')}\n </SelectItem>\n </SelectContent>\n </Select>\n <Select\n value={\n projectFilters.archived === undefined\n ? 'all'\n : projectFilters.archived\n ? 'true'\n : 'false'\n }\n onValueChange={handleArchivedFilter}\n >\n <SelectTrigger className=\"w-full sm:w-[180px]\">\n <SelectValue\n placeholder={tWithFallback(t, 'projectsList.filterByArchive', 'Filter by archive')}\n />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">\n {tWithFallback(t, 'projectsList.archiveAll', 'All Projects')}\n </SelectItem>\n <SelectItem value=\"false\">\n {tWithFallback(t, 'projectsList.archiveActiveProjects', 'Active Projects')}\n </SelectItem>\n <SelectItem value=\"true\">\n {tWithFallback(t, 'projectsList.archiveArchived', 'Archived')}\n </SelectItem>\n </SelectContent>\n </Select>\n {(projectFilters.search ||\n projectFilters.status ||\n projectFilters.archived !== undefined ||\n activeTagFilters.length > 0) && (\n <Button variant=\"outline\" onClick={clearAllProjectFilters}>\n {tWithFallback(t, 'projectsList.clearFilters', 'Clear Filters')}\n </Button>\n )}\n </div>\n\n {activeTagFilters.length > 0 && (\n <div className=\"flex flex-col gap-3 rounded-lg border border-border-subtle bg-bg-surface px-4 py-3\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <p className=\"text-sm font-medium\">\n {tWithFallback(t, 'projectsList.activeTagFilters', 'Active tag filters')}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {tWithFallback(\n t,\n 'projectsList.activeTagFiltersDescription',\n 'Click a project tag to add or remove it from the current filter set.'\n )}\n </p>\n </div>\n <Select\n value={matchAllTagFilters ? 'all' : 'any'}\n onValueChange={(value) => setMatchAllTagFilters(value === 'all')}\n >\n <SelectTrigger className=\"w-full sm:w-[180px]\">\n <SelectValue\n placeholder={tWithFallback(t, 'projectsList.tagMatchMode', 'Tag match mode')}\n />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">\n {tWithFallback(t, 'projectsList.tagMatchAll', 'Match all tags')}\n </SelectItem>\n <SelectItem value=\"any\">\n {tWithFallback(t, 'projectsList.tagMatchAny', 'Match any tag')}\n </SelectItem>\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"flex flex-wrap gap-2\">\n {activeTagFilters.map((tag) => (\n <TagBadge\n key={tag.tagId}\n id={tag.tagId}\n label={tag.label}\n color={tag.color ?? undefined}\n icon={tag.icon ?? undefined}\n description={tag.description ?? undefined}\n removable\n onRemove={() => toggleTagFilter(tag)}\n size=\"sm\"\n />\n ))}\n </div>\n </div>\n )}\n\n {/* Error State */}\n {error && (\n <Card className=\"border-status-error-border\">\n <CardContent className=\"pt-6\">\n <div className=\"flex flex-col items-center justify-center py-8 text-center\">\n <p className=\"text-status-error-text font-semibold mb-2\">\n {tWithFallback(t, 'projectsList.errorLoading', 'Error loading projects')}\n </p>\n <p className=\"text-sm text-text-secondary\">\n {error instanceof Error\n ? error.message\n : tWithFallback(t, 'projectsList.unknownError', 'An unknown error occurred')}\n </p>\n </div>\n </CardContent>\n </Card>\n )}\n\n {/* Loading State */}\n {isLoading && (\n <div className=\"grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4\">\n {Array.from({ length: 6 }).map((_, i) => (\n <Card key={i}>\n <CardContent className=\"pt-6\">\n <div className=\"space-y-3\">\n <Skeleton className=\"size-12 rounded-lg\" />\n <Skeleton className=\"h-4 w-3/4\" />\n <Skeleton className=\"h-4 w-1/2\" />\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n )}\n\n {/* Empty State */}\n {!isLoading && !error && data?.items.length === 0 && (\n <Card>\n <CardContent className=\"pt-6\">\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <div className=\"rounded-full bg-bg-sunken p-6 mb-4\">\n <FolderKanban size={48} className=\"text-text-secondary\" />\n </div>\n <h3 className=\"text-lg font-semibold mb-2\">\n {tWithFallback(t, 'projectsList.noProjectsFound', 'No projects found')}\n </h3>\n <p className=\"text-text-secondary max-w-sm mb-4\">\n {projectFilters.search\n ? tWithFallback(\n t,\n 'projectsList.adjustSearchOrFilters',\n 'Try adjusting your search or filters'\n )\n : tWithFallback(\n t,\n 'projectsList.createFirstProject',\n 'Create your first project to get started organizing your work'\n )}\n </p>\n {!projectFilters.search && canCreateProject && (\n <Button onClick={handleCreateProject}>\n <Plus size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectsList.createProject', 'Create Project')}\n </Button>\n )}\n </div>\n </CardContent>\n </Card>\n )}\n\n {/* Projects Grid */}\n {!isLoading && !error && data && data.items.length > 0 && (\n <>\n <div className=\"flex flex-col gap-3 rounded-lg border bg-bg-surface p-4 sm:flex-row sm:items-center sm:justify-between\">\n <label className=\"flex items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"checkbox\"\n checked={allVisibleSelected}\n ref={(input) => {\n if (input) {\n input.indeterminate = partiallySelected && !allVisibleSelected;\n }\n }}\n onChange={(event) => toggleAllProjects(event.target.checked)}\n aria-label={tWithFallback(\n t,\n 'projectsList.selectAllAriaLabel',\n 'Select all projects'\n )}\n className=\"size-4 rounded border-border-subtle text-primary\"\n />\n <span>\n {tWithFallback(t, 'projectsList.selectAllVisible', 'Select all visible')}\n </span>\n </label>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm text-text-secondary\">\n {selectedProjectIds.size} {tWithFallback(t, 'projectsList.selected', 'selected')}\n </span>\n {selectedProjectIds.size > 0 && (\n <Button variant=\"ghost\" size=\"sm\" onClick={clearProjectSelection}>\n {tWithFallback(t, 'projectsList.clearSelection', 'Clear selection')}\n </Button>\n )}\n {/* Bulk mutations are bound to a single workspaceId via the\n archive/unarchive/delete hooks. On the cross-workspace\n /workspaces/projects route (workspaceId is undefined),\n hide them — selections can span workspaces and the bound\n hook would fail authorization for projects outside the\n caller's context. The per-card actions in ProjectCard\n still work because they resolve per-project. */}\n {workspaceId && canArchiveProject && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleBulkArchive}\n disabled={archivableProjects.length === 0}\n >\n {tWithFallback(t, 'projectsList.archiveSelected', 'Archive Selected')}\n {archivableProjects.length > 0 ? ` (${archivableProjects.length})` : ''}\n </Button>\n )}\n {workspaceId && canArchiveProject && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleBulkUnarchive}\n disabled={unarchivableProjects.length === 0}\n >\n {tWithFallback(t, 'projectsList.unarchiveSelected', 'Unarchive Selected')}\n {unarchivableProjects.length > 0 ? ` (${unarchivableProjects.length})` : ''}\n </Button>\n )}\n {workspaceId && canDeleteProject && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleBulkDelete}\n disabled={selectedProjects.length === 0}\n className=\"text-status-error-text\"\n >\n {tWithFallback(t, 'projectsList.deleteSelected', 'Delete Selected')}\n {selectedProjects.length > 0 ? ` (${selectedProjects.length})` : ''}\n </Button>\n )}\n </div>\n </div>\n\n <div\n className={\n viewMode === 'list'\n ? 'space-y-3'\n : 'grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4'\n }\n >\n {data.items.map((project) => (\n <ProjectCard\n key={project.id}\n project={project}\n selected={selectedProjectIds.has(project.id)}\n canArchive={canArchiveProject}\n canDelete={canDeleteProject}\n onTagClick={toggleTagFilter}\n onToggleSelect={(checked) => {\n const isSelected = selectedProjectIds.has(project.id);\n if (checked !== isSelected) {\n toggleProjectSelection(project.id);\n }\n }}\n />\n ))}\n </div>\n\n {/* Pagination Info */}\n {data.total > data.items.length && (\n <div className=\"text-center text-sm text-text-secondary\">\n {tWithFallback(t, 'projectsList.showingPrefix', 'Showing')} {data.items.length}{' '}\n {tWithFallback(t, 'projectsList.showingOf', 'of')} {data.total}{' '}\n {tWithFallback(t, 'projectsList.showingProjects', 'projects')}\n </div>\n )}\n </>\n )}\n\n {/* Create Project Dialog */}\n {isCreateProjectDialogOpen && <CreateProjectDialog workspaceId={workspaceId} />}\n </div>\n </PageContainer>\n );\n}\n"],"mappings":"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"}
@@ -39,7 +39,7 @@ function k() {
39
39
  className: "text-lg font-semibold",
40
40
  children: r(A, "workspaceDetail.accessDenied", "Access Denied")
41
41
  }), /* @__PURE__ */ _("p", {
42
- className: "text-sm text-muted-foreground",
42
+ className: "text-sm text-text-secondary",
43
43
  children: r(A, "workspaceDetail.accessDeniedDesc", "You don't have permission to view this workspace.")
44
44
  })]
45
45
  })
@@ -65,7 +65,7 @@ function k() {
65
65
  }), /* @__PURE__ */ _("div", {
66
66
  className: "text-center py-12",
67
67
  children: /* @__PURE__ */ _("p", {
68
- className: "text-destructive",
68
+ className: "text-status-error-text",
69
69
  children: r(A, "workspaceDetail.notFound", "Workspace not found")
70
70
  })
71
71
  })]
@@ -125,17 +125,17 @@ function k() {
125
125
  className: "flex items-center gap-2",
126
126
  children: [/* @__PURE__ */ _(f, {
127
127
  size: 20,
128
- className: "text-muted-foreground"
128
+ className: "text-text-secondary"
129
129
  }), r(A, "workspaceDetail.projects", "Projects")]
130
130
  }), /* @__PURE__ */ _(d, {
131
131
  size: 20,
132
- className: "text-muted-foreground"
132
+ className: "text-text-secondary"
133
133
  })]
134
134
  }), /* @__PURE__ */ v(S, { children: [/* @__PURE__ */ _("p", {
135
135
  className: "text-3xl font-bold",
136
136
  children: L?.total ?? P.projectCount ?? 0
137
137
  }), /* @__PURE__ */ _("p", {
138
- className: "text-sm text-muted-foreground",
138
+ className: "text-sm text-text-secondary",
139
139
  children: r(A, "workspaceDetail.projectsDesc", "Projects in this workspace")
140
140
  })] })]
141
141
  }),
@@ -151,17 +151,17 @@ function k() {
151
151
  className: "flex items-center gap-2",
152
152
  children: [/* @__PURE__ */ _(h, {
153
153
  size: 20,
154
- className: "text-muted-foreground"
154
+ className: "text-text-secondary"
155
155
  }), r(A, "workspaceDetail.members", "Members")]
156
156
  }), /* @__PURE__ */ _(d, {
157
157
  size: 20,
158
- className: "text-muted-foreground"
158
+ className: "text-text-secondary"
159
159
  })]
160
160
  }), /* @__PURE__ */ v(S, { children: [/* @__PURE__ */ _("p", {
161
161
  className: "text-3xl font-bold",
162
162
  children: R?.total ?? P.memberCount ?? 0
163
163
  }), /* @__PURE__ */ _("p", {
164
- className: "text-sm text-muted-foreground",
164
+ className: "text-sm text-text-secondary",
165
165
  children: r(A, "workspaceDetail.membersDesc", "Workspace members")
166
166
  })] })]
167
167
  }),
@@ -177,17 +177,17 @@ function k() {
177
177
  className: "flex items-center gap-2",
178
178
  children: [/* @__PURE__ */ _(l, {
179
179
  size: 20,
180
- className: "text-muted-foreground"
180
+ className: "text-text-secondary"
181
181
  }), r(A, "workspaceDetail.activity", "Activity")]
182
182
  }), /* @__PURE__ */ _(d, {
183
183
  size: 20,
184
- className: "text-muted-foreground"
184
+ className: "text-text-secondary"
185
185
  })]
186
186
  }), /* @__PURE__ */ v(S, { children: [/* @__PURE__ */ _("p", {
187
187
  className: "text-3xl font-bold",
188
188
  children: z?.length ?? 0
189
189
  }), /* @__PURE__ */ _("p", {
190
- className: "text-sm text-muted-foreground",
190
+ className: "text-sm text-text-secondary",
191
191
  children: r(A, "workspaceDetail.activityDesc", "Recent activity items")
192
192
  })] })]
193
193
  })
@@ -199,7 +199,7 @@ function k() {
199
199
  className: "grid grid-cols-2 gap-2 text-sm",
200
200
  children: [
201
201
  /* @__PURE__ */ _("span", {
202
- className: "text-muted-foreground",
202
+ className: "text-text-secondary",
203
203
  children: r(A, "workspaceDetail.id", "ID:")
204
204
  }),
205
205
  /* @__PURE__ */ _("span", {
@@ -207,15 +207,15 @@ function k() {
207
207
  children: P.id
208
208
  }),
209
209
  P.domain && /* @__PURE__ */ v(g, { children: [/* @__PURE__ */ _("span", {
210
- className: "text-muted-foreground",
210
+ className: "text-text-secondary",
211
211
  children: r(A, "workspaceDetail.domain", "Domain:")
212
212
  }), /* @__PURE__ */ _("span", { children: P.domain })] }),
213
213
  P.contactEmail && /* @__PURE__ */ v(g, { children: [/* @__PURE__ */ _("span", {
214
- className: "text-muted-foreground",
214
+ className: "text-text-secondary",
215
215
  children: r(A, "workspaceDetail.contactEmail", "Contact Email:")
216
216
  }), /* @__PURE__ */ _("span", { children: P.contactEmail })] }),
217
217
  P.website && /* @__PURE__ */ v(g, { children: [/* @__PURE__ */ _("span", {
218
- className: "text-muted-foreground",
218
+ className: "text-text-secondary",
219
219
  children: r(A, "workspaceDetail.website", "Website:")
220
220
  }), /* @__PURE__ */ _("a", {
221
221
  href: P.website,
@@ -225,7 +225,7 @@ function k() {
225
225
  children: P.website
226
226
  })] }),
227
227
  /* @__PURE__ */ _("span", {
228
- className: "text-muted-foreground",
228
+ className: "text-text-secondary",
229
229
  children: r(A, "workspaceDetail.created", "Created:")
230
230
  }),
231
231
  /* @__PURE__ */ _("span", { children: new Date(P.createdAt).toLocaleDateString() })
@@ -1 +1 @@
1
- {"version":3,"file":"WorkspaceDetailPage.js","names":[],"sources":["../../src/pages/WorkspaceDetailPage.tsx"],"sourcesContent":["import { useParams, useNavigate } from 'react-router-dom';\nimport {\n Settings,\n ArrowLeft,\n FolderKanban,\n Users,\n Activity,\n ChevronRight,\n UserPlus,\n} from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../utils/i18n';\nimport {\n Button,\n Card,\n CardHeader,\n CardTitle,\n CardContent,\n Badge,\n Skeleton,\n} from '@burdenoff/fe-libs/ui';\nimport { useWorkspace } from '../hooks/useWorkspaces';\nimport { useProjects } from '../hooks/useProjects';\nimport { useWorkspaceMembers } from '../hooks/useWorkspaceMembers';\nimport { useWorkspaceActivity } from '../hooks/useWorkspaceActivity';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport { useScopedNavigation } from '../hooks/useScopedNavigation';\nimport { useWorkspacePermissions } from '../hooks/useWorkspacePermissions';\nimport { useWorkspacesPageView } from '../hooks/useWorkspacesPageView';\n\nexport function WorkspaceDetailPage() {\n const { workspaceId } = useParams<{ workspaceId: string }>();\n useWorkspacesPageView('workspaces.detail', { workspaceId, requireParams: true });\n const { t } = useI18n();\n const navigate = useNavigate();\n const { basePath = '/' } = useWorkspacesContext();\n const { withCurrentSearch } = useScopedNavigation();\n const { data: workspace, isLoading, error } = useWorkspace(workspaceId || '');\n const { data: projects } = useProjects({ workspaceId }, { limit: 1 }, workspaceId);\n const { data: members } = useWorkspaceMembers(workspaceId || '', { limit: 1 });\n const { data: activities } = useWorkspaceActivity(workspaceId);\n const {\n canViewWorkspace,\n canUpdateWorkspace,\n canInviteMember,\n isLoading: permissionsLoading,\n } = useWorkspacePermissions();\n\n if (permissionsLoading) {\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n <Skeleton className=\"h-10 w-1/3\" />\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-4\">\n <Skeleton className=\"h-32\" />\n <Skeleton className=\"h-32\" />\n <Skeleton className=\"h-32\" />\n </div>\n </div>\n );\n }\n\n if (!canViewWorkspace) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold\">\n {tWithFallback(t, 'workspaceDetail.accessDenied', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {tWithFallback(\n t,\n 'workspaceDetail.accessDeniedDesc',\n \"You don't have permission to view this workspace.\"\n )}\n </p>\n </div>\n </div>\n );\n }\n\n if (isLoading) {\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n <Skeleton className=\"h-10 w-1/3\" />\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-4\">\n <Skeleton className=\"h-32\" />\n <Skeleton className=\"h-32\" />\n <Skeleton className=\"h-32\" />\n </div>\n </div>\n );\n }\n\n if (error || !workspace) {\n return (\n <div className=\"space-y-4 p-6\">\n <Button\n variant=\"ghost\"\n onClick={() => navigate(withCurrentSearch(`${basePath}/list`))}\n className=\"gap-2\"\n >\n <ArrowLeft size={20} />\n {tWithFallback(t, 'workspaceDetail.backToWorkspaces', 'Back to Workspaces')}\n </Button>\n <div className=\"text-center py-12\">\n <p className=\"text-destructive\">\n {tWithFallback(t, 'workspaceDetail.notFound', 'Workspace not found')}\n </p>\n </div>\n </div>\n );\n }\n\n const handleCardKeyDown = (e: React.KeyboardEvent, path: string) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n navigate(path);\n }\n };\n\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n {/* Header */}\n <div className=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3\">\n <div>\n <Button\n variant=\"ghost\"\n onClick={() => navigate(withCurrentSearch(`${basePath}/list`))}\n className=\"gap-2 mb-2 -ml-2\"\n >\n <ArrowLeft size={20} />\n {tWithFallback(t, 'workspaceDetail.backToWorkspaces', 'Back to Workspaces')}\n </Button>\n <h1 className=\"text-2xl sm:text-3xl font-bold\">{workspace.name}</h1>\n <div className=\"flex items-center gap-2 mt-2\">\n <Badge variant=\"outline\">{workspace.type}</Badge>\n <Badge>{workspace.status}</Badge>\n </div>\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n {canInviteMember ? (\n <Button\n variant=\"outline\"\n onClick={() => navigate(withCurrentSearch(`${basePath}/${workspaceId}/invite`))}\n className=\"gap-2\"\n >\n <UserPlus size={18} />\n {tWithFallback(t, 'workspaceDetail.inviteMembers', 'Invite Member')}\n </Button>\n ) : null}\n {canUpdateWorkspace && (\n <Button\n onClick={() => navigate(withCurrentSearch(`${basePath}/${workspaceId}/settings`))}\n className=\"gap-2\"\n >\n <Settings size={20} />\n {tWithFallback(t, 'workspaceDetail.settings', 'Settings')}\n </Button>\n )}\n </div>\n </div>\n\n {/* Stats Cards */}\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-4\">\n <Card\n className=\"cursor-pointer hover:border-primary/50 hover:shadow-md transition-all duration-200\"\n onClick={() => navigate(withCurrentSearch(`${basePath}/${workspaceId}/projects`))}\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) =>\n handleCardKeyDown(e, withCurrentSearch(`${basePath}/${workspaceId}/projects`))\n }\n >\n <CardHeader className=\"flex flex-row items-center justify-between pb-2\">\n <CardTitle className=\"flex items-center gap-2\">\n <FolderKanban size={20} className=\"text-muted-foreground\" />\n {tWithFallback(t, 'workspaceDetail.projects', 'Projects')}\n </CardTitle>\n <ChevronRight size={20} className=\"text-muted-foreground\" />\n </CardHeader>\n <CardContent>\n <p className=\"text-3xl font-bold\">{projects?.total ?? workspace.projectCount ?? 0}</p>\n <p className=\"text-sm text-muted-foreground\">\n {tWithFallback(t, 'workspaceDetail.projectsDesc', 'Projects in this workspace')}\n </p>\n </CardContent>\n </Card>\n\n <Card\n className=\"cursor-pointer hover:border-primary/50 hover:shadow-md transition-all duration-200\"\n onClick={() => navigate(withCurrentSearch(`${basePath}/${workspaceId}/members`))}\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) =>\n handleCardKeyDown(e, withCurrentSearch(`${basePath}/${workspaceId}/members`))\n }\n >\n <CardHeader className=\"flex flex-row items-center justify-between pb-2\">\n <CardTitle className=\"flex items-center gap-2\">\n <Users size={20} className=\"text-muted-foreground\" />\n {tWithFallback(t, 'workspaceDetail.members', 'Members')}\n </CardTitle>\n <ChevronRight size={20} className=\"text-muted-foreground\" />\n </CardHeader>\n <CardContent>\n <p className=\"text-3xl font-bold\">{members?.total ?? workspace.memberCount ?? 0}</p>\n <p className=\"text-sm text-muted-foreground\">\n {tWithFallback(t, 'workspaceDetail.membersDesc', 'Workspace members')}\n </p>\n </CardContent>\n </Card>\n\n <Card\n className=\"cursor-pointer hover:border-primary/50 hover:shadow-md transition-all duration-200\"\n onClick={() => navigate(withCurrentSearch(`${basePath}/${workspaceId}/activity`))}\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) =>\n handleCardKeyDown(e, withCurrentSearch(`${basePath}/${workspaceId}/activity`))\n }\n >\n <CardHeader className=\"flex flex-row items-center justify-between pb-2\">\n <CardTitle className=\"flex items-center gap-2\">\n <Activity size={20} className=\"text-muted-foreground\" />\n {tWithFallback(t, 'workspaceDetail.activity', 'Activity')}\n </CardTitle>\n <ChevronRight size={20} className=\"text-muted-foreground\" />\n </CardHeader>\n <CardContent>\n <p className=\"text-3xl font-bold\">{activities?.length ?? 0}</p>\n <p className=\"text-sm text-muted-foreground\">\n {tWithFallback(t, 'workspaceDetail.activityDesc', 'Recent activity items')}\n </p>\n </CardContent>\n </Card>\n </div>\n\n {/* Workspace Info */}\n <Card>\n <CardHeader>\n <CardTitle>\n {tWithFallback(t, 'workspaceDetail.workspaceInformation', 'Workspace Information')}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-2\">\n <div className=\"grid grid-cols-2 gap-2 text-sm\">\n <span className=\"text-muted-foreground\">\n {tWithFallback(t, 'workspaceDetail.id', 'ID:')}\n </span>\n <span className=\"font-mono text-xs\">{workspace.id}</span>\n\n {workspace.domain && (\n <>\n <span className=\"text-muted-foreground\">\n {tWithFallback(t, 'workspaceDetail.domain', 'Domain:')}\n </span>\n <span>{workspace.domain}</span>\n </>\n )}\n\n {workspace.contactEmail && (\n <>\n <span className=\"text-muted-foreground\">\n {tWithFallback(t, 'workspaceDetail.contactEmail', 'Contact Email:')}\n </span>\n <span>{workspace.contactEmail}</span>\n </>\n )}\n\n {workspace.website && (\n <>\n <span className=\"text-muted-foreground\">\n {tWithFallback(t, 'workspaceDetail.website', 'Website:')}\n </span>\n <a\n href={workspace.website}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"text-primary hover:underline transition-colors duration-200\"\n >\n {workspace.website}\n </a>\n </>\n )}\n\n <span className=\"text-muted-foreground\">\n {tWithFallback(t, 'workspaceDetail.created', 'Created:')}\n </span>\n <span>{new Date(workspace.createdAt).toLocaleDateString()}</span>\n </div>\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"WorkspaceDetailPage.js","names":[],"sources":["../../src/pages/WorkspaceDetailPage.tsx"],"sourcesContent":["import { useParams, useNavigate } from 'react-router-dom';\nimport {\n Settings,\n ArrowLeft,\n FolderKanban,\n Users,\n Activity,\n ChevronRight,\n UserPlus,\n} from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../utils/i18n';\nimport {\n Button,\n Card,\n CardHeader,\n CardTitle,\n CardContent,\n Badge,\n Skeleton,\n} from '@burdenoff/fe-libs/ui';\nimport { useWorkspace } from '../hooks/useWorkspaces';\nimport { useProjects } from '../hooks/useProjects';\nimport { useWorkspaceMembers } from '../hooks/useWorkspaceMembers';\nimport { useWorkspaceActivity } from '../hooks/useWorkspaceActivity';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport { useScopedNavigation } from '../hooks/useScopedNavigation';\nimport { useWorkspacePermissions } from '../hooks/useWorkspacePermissions';\nimport { useWorkspacesPageView } from '../hooks/useWorkspacesPageView';\n\nexport function WorkspaceDetailPage() {\n const { workspaceId } = useParams<{ workspaceId: string }>();\n useWorkspacesPageView('workspaces.detail', { workspaceId, requireParams: true });\n const { t } = useI18n();\n const navigate = useNavigate();\n const { basePath = '/' } = useWorkspacesContext();\n const { withCurrentSearch } = useScopedNavigation();\n const { data: workspace, isLoading, error } = useWorkspace(workspaceId || '');\n const { data: projects } = useProjects({ workspaceId }, { limit: 1 }, workspaceId);\n const { data: members } = useWorkspaceMembers(workspaceId || '', { limit: 1 });\n const { data: activities } = useWorkspaceActivity(workspaceId);\n const {\n canViewWorkspace,\n canUpdateWorkspace,\n canInviteMember,\n isLoading: permissionsLoading,\n } = useWorkspacePermissions();\n\n if (permissionsLoading) {\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n <Skeleton className=\"h-10 w-1/3\" />\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-4\">\n <Skeleton className=\"h-32\" />\n <Skeleton className=\"h-32\" />\n <Skeleton className=\"h-32\" />\n </div>\n </div>\n );\n }\n\n if (!canViewWorkspace) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold\">\n {tWithFallback(t, 'workspaceDetail.accessDenied', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n 'workspaceDetail.accessDeniedDesc',\n \"You don't have permission to view this workspace.\"\n )}\n </p>\n </div>\n </div>\n );\n }\n\n if (isLoading) {\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n <Skeleton className=\"h-10 w-1/3\" />\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-4\">\n <Skeleton className=\"h-32\" />\n <Skeleton className=\"h-32\" />\n <Skeleton className=\"h-32\" />\n </div>\n </div>\n );\n }\n\n if (error || !workspace) {\n return (\n <div className=\"space-y-4 p-6\">\n <Button\n variant=\"ghost\"\n onClick={() => navigate(withCurrentSearch(`${basePath}/list`))}\n className=\"gap-2\"\n >\n <ArrowLeft size={20} />\n {tWithFallback(t, 'workspaceDetail.backToWorkspaces', 'Back to Workspaces')}\n </Button>\n <div className=\"text-center py-12\">\n <p className=\"text-status-error-text\">\n {tWithFallback(t, 'workspaceDetail.notFound', 'Workspace not found')}\n </p>\n </div>\n </div>\n );\n }\n\n const handleCardKeyDown = (e: React.KeyboardEvent, path: string) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n navigate(path);\n }\n };\n\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n {/* Header */}\n <div className=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3\">\n <div>\n <Button\n variant=\"ghost\"\n onClick={() => navigate(withCurrentSearch(`${basePath}/list`))}\n className=\"gap-2 mb-2 -ml-2\"\n >\n <ArrowLeft size={20} />\n {tWithFallback(t, 'workspaceDetail.backToWorkspaces', 'Back to Workspaces')}\n </Button>\n <h1 className=\"text-2xl sm:text-3xl font-bold\">{workspace.name}</h1>\n <div className=\"flex items-center gap-2 mt-2\">\n <Badge variant=\"outline\">{workspace.type}</Badge>\n <Badge>{workspace.status}</Badge>\n </div>\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n {canInviteMember ? (\n <Button\n variant=\"outline\"\n onClick={() => navigate(withCurrentSearch(`${basePath}/${workspaceId}/invite`))}\n className=\"gap-2\"\n >\n <UserPlus size={18} />\n {tWithFallback(t, 'workspaceDetail.inviteMembers', 'Invite Member')}\n </Button>\n ) : null}\n {canUpdateWorkspace && (\n <Button\n onClick={() => navigate(withCurrentSearch(`${basePath}/${workspaceId}/settings`))}\n className=\"gap-2\"\n >\n <Settings size={20} />\n {tWithFallback(t, 'workspaceDetail.settings', 'Settings')}\n </Button>\n )}\n </div>\n </div>\n\n {/* Stats Cards */}\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-4\">\n <Card\n className=\"cursor-pointer hover:border-primary/50 hover:shadow-md transition-all duration-200\"\n onClick={() => navigate(withCurrentSearch(`${basePath}/${workspaceId}/projects`))}\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) =>\n handleCardKeyDown(e, withCurrentSearch(`${basePath}/${workspaceId}/projects`))\n }\n >\n <CardHeader className=\"flex flex-row items-center justify-between pb-2\">\n <CardTitle className=\"flex items-center gap-2\">\n <FolderKanban size={20} className=\"text-text-secondary\" />\n {tWithFallback(t, 'workspaceDetail.projects', 'Projects')}\n </CardTitle>\n <ChevronRight size={20} className=\"text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <p className=\"text-3xl font-bold\">{projects?.total ?? workspace.projectCount ?? 0}</p>\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(t, 'workspaceDetail.projectsDesc', 'Projects in this workspace')}\n </p>\n </CardContent>\n </Card>\n\n <Card\n className=\"cursor-pointer hover:border-primary/50 hover:shadow-md transition-all duration-200\"\n onClick={() => navigate(withCurrentSearch(`${basePath}/${workspaceId}/members`))}\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) =>\n handleCardKeyDown(e, withCurrentSearch(`${basePath}/${workspaceId}/members`))\n }\n >\n <CardHeader className=\"flex flex-row items-center justify-between pb-2\">\n <CardTitle className=\"flex items-center gap-2\">\n <Users size={20} className=\"text-text-secondary\" />\n {tWithFallback(t, 'workspaceDetail.members', 'Members')}\n </CardTitle>\n <ChevronRight size={20} className=\"text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <p className=\"text-3xl font-bold\">{members?.total ?? workspace.memberCount ?? 0}</p>\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(t, 'workspaceDetail.membersDesc', 'Workspace members')}\n </p>\n </CardContent>\n </Card>\n\n <Card\n className=\"cursor-pointer hover:border-primary/50 hover:shadow-md transition-all duration-200\"\n onClick={() => navigate(withCurrentSearch(`${basePath}/${workspaceId}/activity`))}\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) =>\n handleCardKeyDown(e, withCurrentSearch(`${basePath}/${workspaceId}/activity`))\n }\n >\n <CardHeader className=\"flex flex-row items-center justify-between pb-2\">\n <CardTitle className=\"flex items-center gap-2\">\n <Activity size={20} className=\"text-text-secondary\" />\n {tWithFallback(t, 'workspaceDetail.activity', 'Activity')}\n </CardTitle>\n <ChevronRight size={20} className=\"text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <p className=\"text-3xl font-bold\">{activities?.length ?? 0}</p>\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(t, 'workspaceDetail.activityDesc', 'Recent activity items')}\n </p>\n </CardContent>\n </Card>\n </div>\n\n {/* Workspace Info */}\n <Card>\n <CardHeader>\n <CardTitle>\n {tWithFallback(t, 'workspaceDetail.workspaceInformation', 'Workspace Information')}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-2\">\n <div className=\"grid grid-cols-2 gap-2 text-sm\">\n <span className=\"text-text-secondary\">\n {tWithFallback(t, 'workspaceDetail.id', 'ID:')}\n </span>\n <span className=\"font-mono text-xs\">{workspace.id}</span>\n\n {workspace.domain && (\n <>\n <span className=\"text-text-secondary\">\n {tWithFallback(t, 'workspaceDetail.domain', 'Domain:')}\n </span>\n <span>{workspace.domain}</span>\n </>\n )}\n\n {workspace.contactEmail && (\n <>\n <span className=\"text-text-secondary\">\n {tWithFallback(t, 'workspaceDetail.contactEmail', 'Contact Email:')}\n </span>\n <span>{workspace.contactEmail}</span>\n </>\n )}\n\n {workspace.website && (\n <>\n <span className=\"text-text-secondary\">\n {tWithFallback(t, 'workspaceDetail.website', 'Website:')}\n </span>\n <a\n href={workspace.website}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"text-primary hover:underline transition-colors duration-200\"\n >\n {workspace.website}\n </a>\n </>\n )}\n\n <span className=\"text-text-secondary\">\n {tWithFallback(t, 'workspaceDetail.created', 'Created:')}\n </span>\n <span>{new Date(workspace.createdAt).toLocaleDateString()}</span>\n </div>\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}